home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 5 / Amiga Tools 5.iso / internet-tools / httpproxy / src / httpproxy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-13  |  69.9 KB  |  2,440 lines

  1. /*(( "Header" */
  2. /*
  3.  * $Id: httpproxy.c,v 0.12 1996/02/12 19:23:36 mshopf Exp mshopf $
  4.  *
  5.  * (c) 1995 Matthias Hopf
  6.  *
  7.  * A small little Http Proxy.
  8.  * It can serv as a ProxyProxy, too (i.e. it can perform only caching and will
  9.  * get its data from another proxy).
  10.  * That way it can be used for other protocol types than html, too.
  11.  *
  12.  * Run it standalone at high priority. It won't need much computing time as
  13.  * it does no busy wait at all.
  14.  * If you really want to re-get an already cached page, just reload it immedeately.
  15.  * (no other request inbetween and no more than ReloadTime seconds delay).
  16.  * If you browse offline, you'll get a note that the cache is invalid. Reload the
  17.  * page if you want to queue the page and get the old cache.
  18.  */
  19.  
  20. /*
  21.  * $Log: httpproxy.c,v $
  22.  * Revision 0.12  1996/02/12  19:23:36  mshopf
  23.  * assert() to logfile and debugfile and stderr.
  24.  * MaxRequests now variable.
  25.  * ReadCacheList()/SaveCacheList().
  26.  * lots of bug fixes and cosmetic changes.
  27.  *
  28.  * Revision 0.11  1996/01/15  22:27:58  mshopf
  29.  * fixed AmiTCP4.0 broken time() / stat() times (fix by Fionn Behrens).
  30.  * another couple of bug fixes.
  31.  * added dynamic cache table allocation.
  32.  * error replies still getting better.
  33.  * removed host realname lookup.
  34.  *
  35.  * Revision 0.10  1996/01/09  17:20:35  mshopf
  36.  * Three major and some other minor bug fixes.
  37.  * Added UrlBuffer for POST method and non-proxyproxy support.
  38.  * More and more comformant error messages.
  39.  * basic POST support (only proxying, no caching, no queuing).
  40.  * Sending data even when request is not completed yet.
  41.  * Recreating URL even from nonconformant requests.
  42.  * Long requests supported now.
  43.  * Some cosmetic changes.
  44.  *
  45.  * Revision 0.9  1995/12/06  19:53:55  mshopf
  46.  * added fixes for unix machines and AmiTCP4.0 compilation.
  47.  * some bug fixes.
  48.  * more html conform now.
  49.  * lots of printf type fixes.
  50.  *
  51.  * Revision 0.8  1995/12/03  14:20:23  mshopf
  52.  * Made auto requests and proxy messages more http conform.
  53.  *
  54.  * Revision 0.7  1995/11/19  17:57:50  mshopf
  55.  * added revbump compatible version string.
  56.  * fixed ftp offline proxyproxy std port bug.
  57.  *
  58.  * Revision 0.6  1995/11/04  11:26:13  mshopf
  59.  * better shutdown (requeueing of current transmissions). small bug fixes.
  60.  *
  61.  * Revision 0.5  1995/11/02  18:26:13  mshopf
  62.  * queueing system implemented.
  63.  *
  64.  * Revision 0.4  1995/10/21  21:28:37  mshopf
  65.  * small bug fix.
  66.  *
  67.  * Revision 0.3  1995/10/17  19:23:09  mshopf
  68.  * cleaned up messy logging system.
  69.  * new option 'log'.
  70.  *
  71.  * Revision 0.2  1995/10/13  18:09:17  mshopf
  72.  * everything works so far, exceptions are noted in the header.
  73.  * :-)
  74.  *
  75.  * Revision 0.1  1995/10/13  10:44:27  mshopf
  76.  * no caching at all right now, but it works fine as a proxyproxy.
  77.  *
  78.  */
  79.  
  80.  
  81. /*)) */
  82. /*(( "Includes" */
  83.  
  84. #include "httpproxy_rev.h"
  85. #include <stdio.h>
  86. #include <stdlib.h>
  87. #include <stddef.h>
  88. #include <errno.h>
  89. #include <string.h>
  90. #include <unistd.h>
  91. #include <time.h>
  92. #include <ctype.h>
  93.  
  94. #include <syslog.h>
  95. #include <dirent.h>
  96. #include <sys/stat.h>
  97. #include <sys/syslog.h>
  98. #include <sys/types.h>
  99. #include <sys/time.h>
  100. #include <sys/ioctl.h>
  101. #include <sys/socket.h>
  102. #include <netdb.h>
  103. #include <netinet/in.h>
  104. #include <arpa/inet.h>
  105.  
  106. #ifdef _AMIGA
  107. #  include <bsdsocket.h>
  108. #  define ioctl IoctlSocket
  109. #  define ioctl_t long
  110. #  define strcasecmp stricmp
  111. #  ifdef sys_errlist
  112. #    define strerror(x) (sys_errlist [x])     /* Won't necessary for AmiTCP4.0 */
  113. #  endif
  114. #  define CURRENTDIR ""
  115. typedef long len_t;
  116.  
  117. #  ifdef FIXTIME
  118. /* Fix broken AmiTCP4.0 (or SasC+AmiTCP4.0) time() / stat() */
  119. #    include <proto/dos.h>
  120.  
  121. time_t time (time_t *TimePtr)
  122. {
  123.     struct DateStamp ds;
  124.     time_t t;
  125.     DateStamp (&ds);
  126.  
  127.     /* Sekunden + Minuten*60 +
  128.      * (Tage + offset zu Unix time()[2922 Tage]) * 60 * 60 * 24  */
  129.     t = ds.ds_Tick / 50+ds.ds_Minute*60+(ds.ds_Days+2922)*86400;
  130.     if (TimePtr)
  131.     *TimePtr = t;
  132.     return (t);
  133. }
  134. #  endif
  135.  
  136. #else
  137. #  define ioctl_t int
  138. #  define CloseSocket close
  139. #  define CURRENTDIR "."
  140. typedef int len_t;
  141. #endif
  142.  
  143. #ifdef DEBUG
  144. #  define debug(x)   printf x
  145. #else
  146. #  define debug(x)   ((void) 0)
  147. #endif
  148.  
  149. /* own assert() writing assertation to logfile */
  150. #ifndef NDEBUG
  151. #  define assert(x)  ASSERT ((int)(x), #x, __FILE__, __LINE__)
  152. void ASSERT (int x, const char *text, const char *file, int line)
  153. {
  154.     extern FILE *LogStream;
  155.     if (x)
  156.     return;
  157.     if (! text)
  158.     text = "unknown";
  159.     fprintf (stderr, "assertion (%s) failed in '%s' on line %d\n", text, file, line);
  160.     debug   ((       "assertion (%s) failed in '%s' on line %d\n", text, file, line));
  161.     if (LogStream)
  162.     {
  163.     fprintf (LogStream, "assertion (%s) failed in '%s' on line %d\n", text, file, line);
  164.     fclose  (LogStream);
  165.     LogStream = NULL;
  166.     }
  167.     exit (20);
  168.     /*NOTREACHED*/
  169. }
  170. #else
  171. #  define assert(x)  ((void) 0)
  172. #endif
  173.  
  174. /*)) */
  175. /*(( "Types / Variables / Constants" */
  176.  
  177. /* Types and constants */
  178.  
  179. #define NAME_CACHETABLE   ".cachetable"
  180. #define DEFAULT_PROXYPORT 8080
  181. #define DEFAULT_HTTPPORT  80
  182. #define DEFAULT_DELTIME  (48*60*60)    /* default: delete cached files on startup */
  183. #define DEFAULT_EXPIRETIME (24*60*60)  /* default: delete cache, when the page is requested and too old */
  184. #define DEFAULT_RELOADTIME 10          /* default: expire cache on reload inbetween */
  185. #define DEFAULT_MAX_REQUESTS 8         /* Maximum number of pending requests */
  186.  
  187. #define MIN_REQUESTS     4        /* Minimum number of free requests for interactive actions */
  188. #define MAX_CACHES       32       /* maximum number of cache slots per cachearray */
  189. #define MAX_FILENAME     20       /* maximum size of filename (>= 20) */
  190. #define MAX_URLSAVE      128      /* maximum size of saved url requests */
  191. #define MAX_URLBUFFER    512      /* no Url of any request may be larger than this value (<=MAX_REQBUFFER) */
  192. #define MAX_REQBUFFER    1024
  193. #define MAX_DATABUFFER   2048
  194. #define SHIFT_DATABUFFER 512      /* when x bytes are in the data buffer, shift it after send() */
  195. #define SHIFT_REQBUFFER  512      /* dto. for the request buffer */
  196.  
  197. #define REQ_REQSOCKET    0x01     /* a request socket is open */
  198. #define REQ_CONNSOCKET   0x02     /* a connection socket is open / to be connected */
  199. #define REQ_DONE         0x04     /* the connection socket is already closed */
  200. #define REQ_REQDONE      0x08     /* the URL is completely read (cache may be sent) */
  201. #define REQ_HTTP1X0      0x10     /* the URL was sent with HTTP/1.0 */
  202. #define REQ_URLDONE      0x40     /* the URL request (the first) line is already there */
  203.  
  204. #define CACHE_VALID      0x01     /* Cache is filled and ready to serve */
  205. #define CACHE_QUEUED     0x02     /* Cache is already queued for regetting */
  206. #define CACHE_DELETETMP  0x04     /* There's a temporaray cache file to be removed */
  207.  
  208. #ifndef FALSE
  209. #define FALSE            (0)
  210. #define TRUE             (1)
  211. #endif
  212.                   /* Needed Memory */
  213. #define NEED_MEM         ((MaxRequests * sizeof (request_t) + sizeof (cachearray_t)) / 1024)
  214.  
  215. #define difftime(x,y)    (((u_long)(x)) - ((u_long)(y)))
  216. #define isvalidhttp(x)   ((unsigned char)(x) > ' ' && (x) != '%' && (x) != '\\' && (unsigned char)(x) < 128)
  217.  
  218. /*
  219.  * Some typical Flag combinations in typical order:
  220.  * None:                           Empty slot
  221.  * REQ_REQSOCKET:                  Awaiting URL
  222.  * REQ_REQSOCKET | REQ_CONNSOCKET: Still geting URL, but 1. line is already there
  223.  *                                   *or*  URL done
  224.  *                                 Awaiting connection to remote host
  225.  *                                   *or*  already transfering data
  226.  * REQ_REQSOCKET | REQ_DONE:       The connection socket is already closed, but data is
  227.  *                                   still to be delivered, or data is sent from the cache
  228.  * REQ_CONNSOCKET:                 Request was terminated, still geting data to fill up
  229.  *                                   the cache (TIMEOUT needed!!!)
  230.  */
  231.  
  232. /* REQ_REQSOCKET: alone will never occour on ProxyProxy==TRUE... */
  233.  
  234.  
  235. typedef struct {
  236.            int  Flags;
  237.            char File [MAX_FILENAME];        /* "" if free slot / with begining '_' (data) */
  238.            char Url  [MAX_URLSAVE];         /* "" if free slot or just getting data */
  239.            } cache_t;
  240.  
  241. typedef struct cachearray {
  242.                   struct cachearray *Next;
  243.                   cache_t Caches [MAX_CACHES];
  244.               } cachearray_t;
  245.  
  246. typedef struct {
  247.            long  ReqSocket, ConnSocket;
  248.            int   Flags;
  249.            char  ReqBuffer  [MAX_REQBUFFER];
  250.            char  UrlBuffer  [MAX_URLBUFFER];/* This one contains the URL line for sending the request */
  251.            char  DataBuffer [MAX_DATABUFFER];
  252.            int   ReqSent, ReqRecv;
  253.            int   UrlSent, UrlRecv;
  254.            int   DataSent, DataRecv;
  255.            cache_t *Cache;                  /* NULL if not cacheable */
  256.            FILE  *Stream;                   /* != NULL on open data transfer to/from cache */
  257.            } request_t;
  258.  
  259.  
  260. /* Global variables */
  261.  
  262. char      *VVersion= VERSTAG;
  263. char      *Version = VERSTRING;
  264. FILE      *LogStream = NULL;
  265. char      *PrgName;
  266. long      ServerSocket;
  267. int       StdHttpPort;
  268. int       ServerPort    = DEFAULT_PROXYPORT;
  269. int       ProxyProxy    = FALSE;                    /* TRUE, when all requests should be
  270.                              * forwarded to another proxy. */
  271. struct sockaddr_in ProxyProxyIn;
  272.  
  273. cache_t   *LastCache    = NULL;
  274. time_t    ThisRequestTime = 0;
  275. time_t    StartUpTime;
  276. int       MaxRequests   = DEFAULT_MAX_REQUESTS;
  277. int       RequestsFree  = -1;
  278. int       CachesFree    = MAX_CACHES;
  279. long      CacheNr       = 0;
  280. u_long    DelCacheTime  = DEFAULT_DELTIME;
  281. u_long    ExpireCacheTime = DEFAULT_EXPIRETIME;
  282. u_long    ReloadCacheTime = DEFAULT_RELOADTIME;
  283. int       OffLine = 0;                              /* 0: get cache files again, when they are too old */
  284.                             /* 1: keep cache files and queue them */
  285. int       GetQueued = 0;                            /* 1: get queued data from remote hosts or proxyproxy */
  286. int       CacheUnreadRequests = FALSE;              /* 1: keep cache data on data connection close with data in url send buffer */
  287. request_t *Requests;
  288. cachearray_t *Caches;
  289.  
  290.  
  291. void DeleteConnect (request_t *Req, int ok);
  292. void SaveCacheUrl (cache_t *c);
  293.  
  294. /*)) */
  295.  
  296. /*(( "Init ()" */
  297.  
  298. /* Init all global variables, open server port */
  299.  
  300. void Init (char *ProxyProxyHost, int ProxyProxyPort, char *LogName)
  301. {
  302.     struct servent  *ServEnt;
  303.     struct sockaddr_in SockIn;
  304.     struct hostent  *HostEnt;
  305.     ioctl_t on = 1;
  306.  
  307.     /* Standard setups */
  308.  
  309.     if (! (LogStream = fopen (LogName, "a+")) )
  310.     {
  311.     fprintf (stderr, "cannot open logfile '%s': %s\n", LogName, strerror (errno));
  312.     exit (20);
  313.     }
  314.  
  315.     if (! (Requests = calloc (MaxRequests, sizeof (request_t))) )
  316.     {
  317.     fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
  318.     exit (20);
  319.     }
  320.     RequestsFree = MaxRequests;
  321.  
  322.     if (! (Caches = calloc (1, sizeof (cachearray_t))) )
  323.     {
  324.     fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
  325.     exit (20);
  326.     }
  327.     assert (Caches->Next == NULL);
  328.  
  329.     ThisRequestTime = StartUpTime = time (NULL);
  330.  
  331.     /* Get standard HTTP port (80 at the time of development...but you never know) */
  332.  
  333.     if (! (ServEnt = getservbyname ("http", "tcp")) )
  334.     {
  335.     StdHttpPort = DEFAULT_HTTPPORT;
  336.     syslog (LOG_WARNING, "%s: unknown protocol 'http', using default port %d", PrgName, DEFAULT_HTTPPORT);
  337.     }
  338.     else
  339.     StdHttpPort = ServEnt->s_port;
  340.  
  341.     /* Create Serversocket */
  342.  
  343.     if ( (ServerSocket = socket (PF_INET, SOCK_STREAM, 0)) < 0)
  344.     {
  345.     fprintf (stderr, "socket() for serverport failed: %s\n", strerror (errno));
  346.     exit (20);
  347.     }
  348.  
  349.     memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
  350.     SockIn.sin_family      = AF_INET;
  351.     SockIn.sin_addr.s_addr = INADDR_ANY;
  352.     SockIn.sin_port        = htons (ServerPort);
  353.     if (bind (ServerSocket, (struct sockaddr *) &SockIn, sizeof (struct sockaddr_in)) < 0)
  354.     {
  355.     fprintf (stderr, "bind() failed for port %d: %s\n", ServerPort, strerror (errno));
  356.     exit (20);
  357.     }
  358.  
  359. #ifdef FIOASYNC
  360.     if (ioctl (ServerSocket, FIOASYNC, (caddr_t) &on) < 0)
  361.     {
  362.     fprintf (stderr, "ioctl() failed for FIOASYNC: %s\n", strerror (errno));
  363.     exit (20);
  364.     }
  365. #endif
  366. #ifdef FIONBIO
  367.     if (ioctl (ServerSocket, FIONBIO, (caddr_t) &on) < 0)
  368.     {
  369.     fprintf (stderr, "ioctl() failed for FIONBIO: %s\n", strerror (errno));
  370.     exit (20);
  371.     }
  372. #endif
  373.  
  374.     if (listen (ServerSocket, 4) < 0)
  375.     {
  376.     fprintf (stderr, "listen() failed: %s\n", strerror (errno));
  377.     exit (20);
  378.     }
  379.  
  380.     /* Get ProxyProxy address */
  381.  
  382.     if (ProxyProxyHost)
  383.     {
  384.     ProxyProxy = TRUE;
  385.  
  386.     if (! OffLine)
  387.     {
  388.         memset ((char *) &ProxyProxyIn, 0, sizeof (struct sockaddr_in));
  389.         ProxyProxyIn.sin_family     = AF_INET;
  390.         ProxyProxyIn.sin_port       = htons (ProxyProxyPort);
  391.  
  392.         if ( (ProxyProxyIn.sin_addr.s_addr = inet_addr (ProxyProxyHost)) +0 == -1)                          /* !!! */
  393.         {
  394.         if (! (HostEnt = gethostbyname (ProxyProxyHost)) )
  395.         {
  396.             fprintf (stderr, "can't get proxyproxy host '%s'\n", ProxyProxyHost);
  397.             exit (20);
  398.         }
  399.         else
  400.             memmove (&ProxyProxyIn.sin_addr, HostEnt->h_addr, sizeof (struct in_addr));
  401.         }
  402.         fprintf (LogStream, "%s\nstarting with proxyproxy host '%s', port %d\n",
  403.              Version, ProxyProxyHost, ProxyProxyPort);
  404.         debug (("%s\nstarting with proxyproxy host '%s', port %d\n",
  405.             Version, ProxyProxyHost, ProxyProxyPort));
  406.         return;
  407.     }
  408.     }
  409.     else if (! OffLine)
  410.     {
  411.     fprintf (LogStream, "%s\nstarting in normal mode\n", Version);
  412.     debug (("%s\nstarting in normal mode\n", Version));
  413.     return;
  414.     }
  415.  
  416.     fprintf (LogStream, "%s\nstarting in offline mode\n", Version);
  417.     debug  (("%s\nstarting in offline mode\n", Version));
  418. }
  419.  
  420.  
  421. /*)) */
  422. /*(( "InitCacheSlot()/GetFreeCacheSlot()/ErrToReq()" */
  423.  
  424. /* Initialize cache slot */
  425.  
  426. void InitCacheSlot (cache_t *c, cache_t *Template, char Init, char Separator)
  427. {
  428.     if (! Template)
  429.     {
  430.     c->Flags  = 0;
  431.     c->Url[0] = '\0';
  432.     sprintf (c->File, "%c%08lx%c%08lx", Init, StartUpTime, Separator, CacheNr++);
  433.     debug (("new cache allocated: '%s'\n", c->File));
  434.     }
  435.     else
  436.     {
  437.     c->Flags  = Template->Flags & ~CACHE_VALID;
  438.     strcpy  (c->Url, Template->Url);
  439.     sprintf (c->File, "%c%8.8s%c%8.8s", Init, & Template->File [1], Separator, & Template->File [10]);
  440.     debug (("cache %s from template %s\n", c->File, Template->File));
  441.     }
  442. }
  443.  
  444.  
  445. /* Scan for a free cache slot */
  446.  
  447. cache_t *GetFreeCacheSlot (void)
  448. {
  449.     cache_t *c;
  450.     cachearray_t *a = Caches;
  451.     int i;
  452.  
  453.     debug (("new cache requested.\n"));
  454.     if (! CachesFree)
  455.     {
  456.     while (a->Next)
  457.         a = a->Next;
  458.  
  459.     if (! (a->Next = calloc (1, sizeof (cachearray_t))) )
  460.     {
  461.         syslog (LOG_NOTICE, "%s: no more memory for cache arrays", PrgName);
  462.         return (NULL);
  463.     }
  464.     assert (a->Next->Next == NULL);
  465.     debug (("allocated %d new cache slots at 0x%x\n", MAX_CACHES, a->Next));
  466.     a = a->Next;
  467.     CachesFree += MAX_CACHES - 1;
  468.     c = & a->Caches [0];
  469.     InitCacheSlot (c, NULL, '_', '.');
  470.     return (& a->Caches [0]);
  471.     }
  472.  
  473.     while (a)
  474.     {
  475.     for (c = a->Caches, i=0; i < MAX_CACHES; c++, i++)
  476.         if (c->Url[0] == '\0' && c->File[0] == '\0')
  477.         {
  478.         CachesFree--;
  479.         InitCacheSlot (c, NULL, '_', '.');
  480.         return (c);
  481.         }
  482.     a = a->Next;
  483.     }
  484.     assert (0);      /*NOTREACHED*/
  485. }
  486.  
  487.  
  488. /* Type an error to a data buffer of a pending request and set everything up.
  489.  * The error is printed into the Logstream, too. When Short == NULL, a information
  490.  * message is sent, and nothing is printed into the Logstream. */
  491.  
  492. void   ErrToReq (request_t *Req, int Number, char *Short, char *Descr)
  493. {
  494.     static char *Author  = "<A HREF=\"http://wwwcip.informatik.uni-erlangen.de/user/mshopf\">Matthias Hopf</A>";
  495.     Req->DataSent = 0;
  496.  
  497.     if (Req->Flags & REQ_HTTP1X0)
  498.     {
  499.     if (Short)
  500.     {
  501.         if (errno > 0)
  502.         {
  503.         fprintf (LogStream, "#%02d: Error: %s: %s\n", Req-Requests, Short, strerror (errno));
  504.         debug (("#%02d: Error: %s: %s\n", Req-Requests, Short, strerror (errno)));
  505.         }
  506.         else
  507.         {
  508.         fprintf (LogStream, "#%02d: Error: %s\n", Req-Requests, Short);
  509.         debug (("#%02d: Error: %s\n", Req-Requests, Short));
  510.         }
  511.  
  512.         sprintf (Req->DataBuffer, "HTTP/1.0 %03d %s\r\n"
  513.              "Server: %s\r\n"
  514.              "Content-Type: text/html\r\n\r\n"
  515.              "<HTML><HEAD><TITLE>Proxy Error</TITLE></HEAD>\n"
  516.              "<BODY><H1>Proxy Error: %s</H1><P>\n"
  517.              "%s%s%s"
  518.              "%s<P>\n"
  519.              "<HR><ADDRESS>%s by %s</ADDRESS>\n"
  520.              "</BODY></HTML>\n",
  521.              Number, Short, VERSHTTP, Short,
  522.              (errno > 0 ? "<H3>Cause: " : ""),
  523.              (errno > 0 ? strerror (errno) : ""),
  524.              (errno > 0 ? "</H3>\n" : ""),
  525.              Descr, Version, Author);
  526.     }
  527.     else
  528.         sprintf (Req->DataBuffer, "HTTP/1.0 %03d Proxy Message\r\n"
  529.              "Server: %s\r\n"
  530.              "Content-Type: text/html\r\n\r\n"
  531.              "<HTML><HEAD><TITLE>Proxy Message</TITLE></HEAD>\n"
  532.              "<BODY><H3>Proxy Message:</H3><P>\n"
  533.              "%s<P>\n"
  534.              "<HR><ADDRESS>%s by %s</ADDRESS>\n"
  535.              "</BODY></HTML>\n",
  536.              Number, VERSHTTP, Descr, Version, Author);
  537.     }
  538.     else
  539.     {
  540.     if (Short)
  541.     {
  542.         fprintf (LogStream, "#%02d: Error for non-Http1.0 request: %s\n", Req-Requests, Short);
  543.         debug (("#%02d: Error for non-Http1.0 request : %s\n", Req-Requests, Short));
  544.  
  545.         sprintf (Req->DataBuffer, "<HTML><HEAD><TITLE>Proxy Error</TITLE></HEAD>\n"
  546.              "<BODY><H1>Proxy Error: %s</H1><P>\n"
  547.              "%s%s%s"
  548.              "%s<P>\n"
  549.              "<HR><ADDRESS>%s by %s</ADDRESS>\n"
  550.              "</BODY></HTML>\n",
  551.              Short,
  552.              (errno > 0 ? "<H3>Cause: " : ""),
  553.              (errno > 0 ? strerror (errno) : ""),
  554.              (errno > 0 ? "</H3>\n" : ""),
  555.              Descr, Version, Author);
  556.     }
  557.     else
  558.         sprintf (Req->DataBuffer, "<HTML><HEAD><TITLE>Proxy Message</TITLE></HEAD>\n"
  559.              "<BODY><H3>Proxy Message:</H3><P>\n"
  560.              "%s<P>\n"
  561.              "<HR><ADDRESS>%s by %s</ADDRESS>\n"
  562.              "</BODY></HTML>\n",
  563.              Descr, Version, Author);
  564.     }
  565.  
  566.     Req->DataRecv = strlen (Req->DataBuffer);
  567.     Req->Flags |= REQ_DONE;
  568.     if (Req->Flags & REQ_CONNSOCKET)
  569.     DeleteConnect (Req, FALSE);
  570. }
  571.  
  572.  
  573. /*)) */
  574. /*(( "RemCacheEntry()/CheckCacheTime()" */
  575.  
  576. /* Remove a cache entry and its according files */
  577.  
  578. void RemCacheEntry (cache_t *c)
  579. {
  580.     cache_t tmp;
  581.  
  582.     debug (("deleting cache file '%s', additional url: %s\n", c->File, c->Url[0] ? "yes" : "no"));
  583.     if (c->File[0] && c->File[0] != '.')                     /* It's no special message file */
  584.     {
  585.     if (remove (c->File))
  586.     {
  587.         fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
  588.         debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
  589.     }
  590.     if (c->Url[0])
  591.     {
  592.         c->File[0] = '@';
  593.         if (remove (c->File))
  594.         {
  595.         fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
  596.         debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
  597.         }
  598.     }
  599.     if (c->Flags & CACHE_DELETETMP)
  600.     {
  601.         InitCacheSlot (&tmp, c, '@', '@');
  602.         debug (("removing temporary url file '%s'\n", tmp.File));
  603.         if (remove (tmp.File))
  604.         {
  605.         fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
  606.         debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
  607.         }
  608.     }
  609.     }
  610.  
  611.     c->File[0] = c->Url[0] = '\0';
  612.     c->Flags = 0;
  613.     CachesFree++;
  614. }
  615.  
  616.  
  617. /* Check the modification time and date of a cache entry; set Time to 0 to force expire. */
  618. /* Returns -1, when the cache entry is expired or to be reloaded or not existing, the entry is removed.
  619.  * Returns -2, when the cache entry is expired and queued. */
  620.  
  621. int CheckCacheTime (cache_t *c, u_long Time)
  622. {
  623.     time_t FileT = -1;
  624.     struct stat s;
  625.  
  626.     if (stat (c->File, &s) >= 0)
  627.     {
  628.     FileT = s.st_mtime;
  629.     debug (("local 0x%lx, access 0x%lx, diff1 %ld <?> %s\n", ThisRequestTime, FileT,
  630.         difftime (ThisRequestTime, FileT), difftime (ThisRequestTime, FileT) > Time ? "expired" : "valid"));
  631.     if ((! Time) || difftime (ThisRequestTime, FileT) > Time)
  632.     {
  633.         if (OffLine)
  634.         {
  635.         if ((c->Flags & CACHE_VALID) && ! (c->Flags & CACHE_QUEUED))
  636.         {                                   /* When CACHE_VALID is not set, the routine was called from buildcache() or similar */
  637.             cache_t tmp;
  638.             c->Flags |= CACHE_QUEUED;
  639.             InitCacheSlot (&tmp, c, '@', '@'); /* Queue Url for cache entry and return -2 */
  640.             SaveCacheUrl (&tmp);
  641.         }
  642.         return (-2);
  643.         }
  644.         else if (c->Flags & CACHE_QUEUED)       /* Is the entry queued and not yet sent? */
  645.         return (OffLine ? -2 : -1);
  646.         else
  647.         FileT = -1;
  648.     }
  649.     }
  650.  
  651.     if (FileT == -1)
  652.     {
  653.     if (c->Flags & CACHE_QUEUED)
  654.     {
  655.         debug (("cache entry '%s' not yet there (queued) - This should not happen...\n", c->File));
  656.         fprintf (stderr, "%s: Warning! Data consistency failure, line %d\n", PrgName, __LINE__);
  657.         return (-2);
  658.     }
  659.     else
  660.     {
  661.         debug (("cache entry '%s' expired / to be reloaded, removing\n", c->File));
  662.         RemCacheEntry (c);
  663.         return (-1);
  664.     }
  665.     }
  666.     return (c->Flags & CACHE_QUEUED ? -2 : 0);
  667. }
  668.  
  669. /*)) */
  670. /*(( "ReadCacheUrl()/SaveCacheUrl()" */
  671.  
  672. /* Read the Url from a specific url file */
  673.  
  674. void ReadCacheUrl (cache_t *c, char *Name)
  675. {
  676.     FILE *f;
  677.  
  678.     c->Url[0] = ' ';              /* RemCacheEntry shall remove the url file, too, in case it is called */
  679.     switch (CheckCacheTime (c, DelCacheTime)) {
  680.     case 0:
  681.     case -3:
  682.     break;
  683.  
  684.     case -1:
  685.     return;
  686.     default:
  687.     RemCacheEntry (c);
  688.     return;
  689.     }
  690.  
  691.     if (! (f = fopen (Name, "r")) )
  692.     {
  693.     fprintf (LogStream, "cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno));
  694.     debug (("cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno)));
  695.     RemCacheEntry (c);
  696.     return;
  697.     }
  698.     if (fgets (c->Url, MAX_URLSAVE, f))
  699.     {
  700.     switch (c->Url [strlen (c->Url) - 1]) {
  701.     case '\n':
  702.     case '\r':
  703.         break;
  704.  
  705.     default:                      /* Cache entry is valid -> return */
  706.         fclose (f);
  707.         c->Flags = CACHE_VALID;
  708.         debug (("valid cache entry: url '%s', data '%s'\n", c->Url, c->File));
  709.         return;
  710.     }
  711.     }
  712.     fclose (f);
  713.     fprintf (LogStream, "corrupt cache entry for url file '%s', removing\n", Name);
  714.     debug (("corrupt cache entry for url file '%s', removing\n", Name));
  715.     RemCacheEntry (c);
  716. }
  717.  
  718.  
  719. /* Save the cache's Url to its url file */
  720.  
  721. void SaveCacheUrl (cache_t *c)
  722. {
  723.     FILE *UrlStream;
  724.     cache_t tmp;
  725.  
  726.     c->File[0] = '@';
  727.     debug (("writing url file '%s'\n", c->File));
  728.     if ( (UrlStream = fopen (c->File, "w")) )
  729.     {
  730.     fprintf (UrlStream, "%s", c->Url);  /* no error checking - well... */
  731.     fclose (UrlStream);
  732.     c->File[0] = '_';
  733.     if (! (c->Flags & CACHE_QUEUED))
  734.         c->Flags |= CACHE_VALID;
  735.         /* fprintf (LogStream, "URL '%s' is now cached\n",
  736.          * c->Url);*/
  737.     if (c->Flags & CACHE_DELETETMP)
  738.     {
  739.         InitCacheSlot (&tmp, c, '@', '@');
  740.         debug (("removing temporary url file '%s'\n", tmp.File));
  741.         if (remove (tmp.File))
  742.         {
  743.         fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
  744.         debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
  745.         }
  746.         c->Flags &= ~CACHE_DELETETMP;
  747.     }
  748.     }
  749.     else
  750.     {
  751.     fprintf (LogStream, "cannot write cache url file '%s': %s\n",
  752.          c->File, strerror (errno));
  753.     debug (("cannot write cache url file '%s': %s\n",
  754.         c->File, strerror (errno)));
  755.     c->File[0] = '_';
  756.     if (c->Flags & CACHE_QUEUED)
  757.         c->File[0] = '\0';
  758.     RemCacheEntry (c);
  759.     }
  760. }
  761.  
  762.  
  763. /*)) */
  764. /*(( "ReadCacheList()/SaveCacheList()" */
  765.  
  766. /* Read the cache list file when it's there */
  767. /* Format: Fields (max size):
  768.  * Filename (MAX_FILENAME) " " Flags (1) Url (MAX_URLSAVE)
  769.  * Terminated with \n. */
  770. /* Flags:   v:Valid   q:Queued   r:Requeued (expired cache entry exists) */
  771. /* <- -1: Failure   0: ok */
  772.  
  773. int ReadCacheList (void)
  774. {
  775.     cache_t *c;
  776.     char Buffer [MAX_URLSAVE+MAX_FILENAME+3];
  777.     FILE *Fd;
  778.     char *p, *End;
  779.  
  780.     if (! (Fd = fopen (NAME_CACHETABLE, "r")))
  781.     return -1;
  782.  
  783.     debug (("Cachetable valid.\n"));
  784.     while (fgets (Buffer, MAX_URLSAVE+MAX_FILENAME+3, Fd))
  785.     {
  786.     End = Buffer + strlen (Buffer) - 1;
  787.     *End = '\0';                                  /* Remove terminating '\n' */
  788.     p = strchr (Buffer, ' ');
  789.     if (p == NULL || p >= & Buffer [MAX_FILENAME])
  790.     {
  791.         fprintf (LogStream, "Invalid line in cachetable, skipping: '%s'\n", Buffer);
  792.         debug (("Invalid line in cachetable, skipping: '%s'\n", Buffer));
  793.         continue;
  794.     }
  795.     p[0] = '\0';
  796.     if (p[1] != 'v' && p[1] != 'q' && p[1] != 'r')
  797.     {
  798.         fprintf (LogStream, "Unknown cache typ in cachetable, skipping: '%s'\n", Buffer);
  799.         debug (("Unknown cache typ in cachetable, skipping: '%s'\n", Buffer));
  800.         continue;
  801.     }
  802.     if (p[2] == '\0')
  803.     {
  804.         fprintf (LogStream, "Invalid line in cachetable, skipping: '%s'\n", Buffer);
  805.         debug (("Invalid line in cachetable, skipping: '%s'\n", Buffer));
  806.         continue;
  807.     }
  808.     assert (&p[2] < End);
  809.     assert (strlen (&p[2]) < MAX_URLSAVE);
  810.  
  811.     if (! (c = GetFreeCacheSlot ()))
  812.         break;
  813.  
  814.     strcpy (c->File, Buffer);
  815.     strcpy (c->Url,  &p[2]);
  816.     if (p[1] == 'v')
  817.         c->Flags = CACHE_VALID;
  818.     else if (p[1] == 'q')
  819.         c->Flags = CACHE_QUEUED;
  820.     else
  821.         c->Flags = CACHE_VALID | CACHE_QUEUED | CACHE_DELETETMP;
  822.     debug (("read '%c' line from cachetable, file '%s', url '%s'\n", p[1], c->File, c->Url));
  823.     }
  824.  
  825.     fclose (Fd);
  826.     remove (NAME_CACHETABLE);
  827.     return 0;
  828. }
  829.  
  830.  
  831. /* Save all cache entries in the cachetable file */
  832.  
  833. void SaveCacheList (void)
  834. {
  835.     cachearray_t *a;
  836.     cache_t      *c;
  837.     int          i;
  838.     FILE         *Fd;
  839.  
  840.     if (! (Fd = fopen (NAME_CACHETABLE, "w")))
  841.     return;
  842.  
  843.     for (a = Caches; a; a = a->Next)
  844.     for (i = 0, c = a->Caches; i < MAX_CACHES; i++, c++)
  845.         if (c->File[0] && c->Url[0])
  846.         {
  847.         if ((c->Flags & CACHE_VALID) && (c->Flags & CACHE_QUEUED))
  848.         {
  849.             if (c->File[0] == '_')
  850.             fprintf (Fd, "%s r%s\n", c->File, c->Url);
  851.             else
  852.             {
  853.             fprintf (LogStream, "Warning! Requeued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  854.             fprintf (stderr,    "Warning! Requeued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  855.             debug             (("Warning! Requeued cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
  856.             }
  857.         }
  858.         else if (c->Flags & CACHE_VALID)
  859.         {
  860.             if (c->File[0] == '_')
  861.             fprintf (Fd, "%s v%s\n", c->File, c->Url);
  862.             else
  863.             {
  864.             fprintf (LogStream, "Warning! Valid cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  865.             fprintf (stderr,    "Warning! Valid cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  866.             debug             (("Warning! Valid cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
  867.             }
  868.         }
  869.         else if (c->Flags & CACHE_QUEUED)
  870.         {
  871.             if (c->File[0] == '@')
  872.             fprintf (Fd, "%s q%s\n", c->File, c->Url);
  873.             else
  874.             {
  875.             fprintf (LogStream, "Warning! Queued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  876.             fprintf (stderr,    "Warning! Queued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  877.             debug             (("Warning! Queued cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
  878.             }
  879.         }
  880.         else
  881.         {
  882.             fprintf (LogStream, "Warning! Unknown cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  883.             fprintf (stderr,    "Warning! Unknown cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
  884.             debug             (("Warning! Unknown cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
  885.         }
  886.         }
  887.  
  888.     fclose (Fd);
  889. }
  890.  
  891. /*)) */
  892. /*(( "BuildCache ()" */
  893.  
  894. /* Scan cache directory, build up cache and delete invalid cache entries */
  895.  
  896. void BuildCache (void)
  897. {
  898.     DIR *Dir;
  899.     cachearray_t *aa, *aaa;
  900.     cache_t *c, *cc, *ccc;
  901.     struct dirent *d;
  902.  
  903.     if (! (Dir = opendir (CURRENTDIR)))
  904.     {
  905.     fprintf (stderr, "%s: cannot open cache directory: %s\n", PrgName, strerror (errno));
  906.     exit (20);
  907.     }
  908.  
  909.     errno = 0;
  910.     c = GetFreeCacheSlot ();
  911.     c->File [0] = '\0';
  912.     assert (c != NULL);
  913.  
  914.     while ( (d = readdir (Dir)) )
  915.     {
  916.     if (strlen (d->d_name) > MAX_FILENAME-1)
  917.     {
  918.         if (d->d_name[0] != '.')                 /* no special file... */
  919.         fprintf (stderr, "unknown file '%s' in cache directory\n", d->d_name);
  920.     }
  921.     else
  922.     {
  923.         strcpy (c->File, d->d_name);
  924.         if (c->File [0] == '_')                  /* found cache data - look for cache url */
  925.         {
  926.         debug (("found data '%s'\n", c->File));
  927.         aa = Caches;
  928.         for (cc = aa->Caches; ; cc++)
  929.         {
  930.             if (cc >= & aa->Caches [MAX_CACHES])
  931.             {
  932.             aa = aa->Next;
  933.             assert (aa != NULL);
  934.             cc = aa->Caches;
  935.             }
  936.             if (cc == c)
  937.             break;
  938.             if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '@')
  939.             {
  940.             cc->File[0] = '_';
  941.             c->File[0]  = '@';
  942.             ReadCacheUrl (cc, c->File);  /* already read... */
  943.             c->File[0] = '\0';
  944.             break;
  945.             }
  946.         }
  947.         if (c == cc)                         /* not found... */
  948.         {
  949.             if (! (c = GetFreeCacheSlot ()) )/* keep it, may be we'll get the url file, too */
  950.             {
  951.             fprintf (LogStream, "Cache table full while recacheing\n");
  952.             debug (("Cache table full while recacheing\n"));
  953.             break;                       /* break from while - no room left... */
  954.             }
  955.             c->File [0] = '\0';
  956.         }
  957.         }
  958.         else if (c->File [0] == '@')             /* found cache url - look for cache data */
  959.         {
  960.         debug (("found url '%s'\n", c->File));
  961.         aa = Caches;
  962.         for (cc = aa->Caches; ; cc++)
  963.         {
  964.             if (cc >= & aa->Caches [MAX_CACHES])
  965.             {
  966.             aa = aa->Next;
  967.             assert (aa != NULL);
  968.             cc = aa->Caches;
  969.             }
  970.             if (cc == c)
  971.             break;
  972.             if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '_')
  973.             {
  974.             ReadCacheUrl (cc, c->File);  /* already read... */
  975.             c->File[0] = '\0';
  976.             break;
  977.             }
  978.         }
  979.         if (c == cc)                         /* not found... */
  980.         {
  981.             if (! (c = GetFreeCacheSlot ())) /* keep it, may be we'll get the data file, too */
  982.             {
  983.             fprintf (LogStream, "Cache table full while recacheing\n");
  984.             debug (("Cache table full while recacheing\n"));
  985.             break;                       /* break from while - no room left... */
  986.             }
  987.             c->File [0] = '\0';
  988.         }
  989.         }
  990.         else
  991.         {
  992.         if (c->File [0] != '.')             /* .files, .httpproxy-log or standard directories (unix) */
  993.             fprintf (stderr, "unknown file '%s' in cache directory\n", c->File);
  994.         c->File[0] = '\0';
  995.         }
  996.     }
  997.     }
  998.     if (errno)
  999.     fprintf (stderr, "directory error while building cache table, continueing: %s\n", strerror (errno));
  1000.     closedir (Dir);
  1001.     RemCacheEntry (c);
  1002.  
  1003.     /* now remove all incomplete cachentries (and files) */
  1004.     aa = Caches;
  1005.     for (cc = aa->Caches; ; cc++)
  1006.     {
  1007.     if (cc >= & aa->Caches [MAX_CACHES])
  1008.     {
  1009.         aa = aa->Next;
  1010.         assert (aa != NULL);
  1011.         cc = aa->Caches;
  1012.     }
  1013.     if (cc == c)
  1014.         break;
  1015.     if (cc->Url[0] == '\0' && cc->File [0] == '_')
  1016.     {
  1017.         fprintf (LogStream, "cache file '%s' invalid, removing.\n", cc->File);
  1018.         debug (("cache file '%s' invalid, removing.\n", cc->File));
  1019.         RemCacheEntry (cc);
  1020.     }
  1021.     }
  1022.  
  1023.     /* mark all url only caches as queued */
  1024.     aa = Caches;
  1025.     for (cc = aa->Caches; ; cc++)
  1026.     {
  1027.     if (cc >= & aa->Caches [MAX_CACHES])
  1028.     {
  1029.         aa = aa->Next;
  1030.         assert (aa != NULL);
  1031.         cc = aa->Caches;
  1032.     }
  1033.     if (cc == c)
  1034.         break;
  1035.     if (cc->File [0] == '@')
  1036.     {
  1037.         ReadCacheUrl (cc, cc->File);         /* the file name remains '@...' */
  1038.         if (cc->File [0] == '@')
  1039.         {
  1040.         cc->Flags = CACHE_QUEUED;        /* and ! CACHE_VAILD */
  1041.         aaa = Caches;
  1042.         for (ccc = aaa->Caches; ; ccc++)
  1043.         {
  1044.             if (ccc >= & aaa->Caches [MAX_CACHES])
  1045.             {
  1046.             aaa = aaa->Next;
  1047.             assert (aaa != NULL);
  1048.             ccc = aaa->Caches;
  1049.             }
  1050.             if (ccc == c)
  1051.             break;
  1052.             if (ccc->File [0] == '_')
  1053.             if (cc != ccc && strcmp (ccc->Url, cc->Url) == 0)
  1054.             {
  1055.                 debug (("url '%s': queued url file %s associated with expired cache entry %s\n", cc->Url, cc->File, ccc->File));
  1056.                 assert (ccc->Flags & CACHE_VALID);
  1057.                 if (cc->File [9] != '@')           /* it has to be a special queued url file... */
  1058.                 {
  1059.                 fprintf (LogStream, "Warning! Double cache consistency error, file '%s', url '%s', file2 '%s'\n", ccc->File, ccc->Url, cc->File);
  1060.                 fprintf (stderr,    "Warning! Double cache consistency error, file '%s', url '%s', file2 '%s'\n", ccc->File, ccc->Url, cc->File);
  1061.                 debug             (("Warning! Double cache consistency error, file '%s', url '%s', file2 '%s'\n", ccc->File, ccc->Url, cc->File));
  1062.                 }
  1063.  
  1064.                 ccc->Flags |= CACHE_QUEUED | CACHE_DELETETMP;
  1065.                 cc->File[0] = '\0';
  1066.                 RemCacheEntry (cc);
  1067.                 ccc = c;         /* break all */
  1068.                 break;
  1069.             }
  1070.         }
  1071.         if (ccc == c)
  1072.             debug (("queued url '%s'\n", cc->Url));
  1073.         }
  1074.     }
  1075.     }
  1076. }
  1077.  
  1078.  
  1079. /*)) */
  1080. /*(( "ScanCache()" */
  1081.  
  1082. /* Scan whether a specific cache is already there, filled and ready to serve */
  1083. /* Returns 0: no, not there  1: yes, all set up  -1: Failure
  1084.  * 2: error message in buffer */
  1085. /* Sets up the request struct for correct filling, opens all streams, etc. */
  1086.  
  1087. int ScanCache (request_t *Req, char *Url, int Known)
  1088. {
  1089.     int i;
  1090.     cache_t *c;
  1091.     cachearray_t *a;
  1092.     static time_t LastRequestTime;
  1093.  
  1094.     debug (("Searching for cache for Url '%s'\n", Url));
  1095.     LastRequestTime = ThisRequestTime;
  1096.     ThisRequestTime = time (NULL);
  1097.     debug (("Last: %ld, This: %ld, Diff: %ld, reload: %s\n", LastRequestTime, ThisRequestTime,
  1098.         difftime (ThisRequestTime, LastRequestTime),
  1099.         difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime ? "yes" : "no"));
  1100.  
  1101.     for (a = Caches; a; a = a->Next)
  1102.     for (c = a->Caches, i=0; i < MAX_CACHES; c++, i++)
  1103.         if (c->Url[0])
  1104.         {
  1105.         if (strcmp (c->Url, Url) == 0)
  1106.         {
  1107.             if (c->Flags & CACHE_VALID)
  1108.             {
  1109.             switch (CheckCacheTime (c, ExpireCacheTime)) {
  1110.             case 0:
  1111.                 if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  1112.                 {
  1113.                 if (CheckCacheTime (c, 0) == -2)         /* force queueing / reloading */
  1114.                 {
  1115.                     debug (("queued url on request\n"));
  1116.                     ErrToReq (Req, 203, NULL, "Your request for reloading the document is queued.<BR>\n"
  1117.                           "You will get the new document next time you are online.<BR>\n"
  1118.                           "An expired cache entry exists and can be viewed by immedeately reloading this document.");
  1119.                     return (2);
  1120.                 }
  1121.                 break;                                   /* forcing reload of url */
  1122.                 }
  1123.  
  1124.                 if (! (Req->Stream = fopen (c->File, "r")) )
  1125.                 {
  1126.                 fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
  1127.                 debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
  1128.                 RemCacheEntry (c);
  1129.                 }
  1130.                 else
  1131.                 {
  1132.                 debug (("Found cache entry, file '%s'\n", c->File));
  1133.                 Req->Cache  = LastCache = c;             /* ok, found cache entry */
  1134.                 Req->Flags |= REQ_DONE;
  1135.                 return (1);
  1136.                 }
  1137.                 break;
  1138.  
  1139.                 /* case -1: break; */
  1140.             case -2:
  1141.                 if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  1142.                 {
  1143.                 debug (("Sending (invalid) cache file '%s' on request\n", c->File));
  1144.                 if (! (Req->Stream = fopen (c->File, "r")) )
  1145.                 {
  1146.                     fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
  1147.                     debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
  1148.                     RemCacheEntry (c);
  1149.                     break;
  1150.                 }
  1151.                 else
  1152.                 {
  1153.                     Req->Cache  = c;                         /* ok, send invalid cache entry */
  1154.                     Req->Flags |= REQ_DONE;
  1155.                     return (1);
  1156.                 }
  1157.                 }
  1158.                 else
  1159.                 {
  1160.                 if (OffLine)
  1161.                 {
  1162.                     debug (("Cache entry expired, queued\n"));
  1163.                     ErrToReq (Req, 203, NULL, "Your request is queued.<BR>\n"
  1164.                           "You will get the document next time you are online.<BR>\n"
  1165.                           "An expired cache entry exists and can be viewed by immedeately reloading this document.");
  1166.                     LastCache = c;
  1167.                     return (2);
  1168.                 }
  1169.                 else
  1170.                 {
  1171.                     debug (("unqueueing and getting Cache entry\n"));
  1172.                     c->Flags   = 0;
  1173.                     c->File[0] = '_';
  1174.                     LastCache  = c;
  1175.                     goto HaveAlreadyCache;         /* sorry... */
  1176.                 }
  1177.                 }
  1178.             }
  1179.             }
  1180.             else                                           /* c->Flags & CACHE_VALID */
  1181.             {
  1182.             if (c->Flags & CACHE_QUEUED)
  1183.             {
  1184.                 if (OffLine)
  1185.                 {
  1186.                 if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
  1187.                 {
  1188.                     errno = 0;
  1189.                     ErrToReq (Req, 404, "Already queued", "Your request is already queued.<BR>\n"
  1190.                           "You will get the document next time you are online.<BR>\n"
  1191.                           "You tried to get an expired cache entry, but this document is not cached right now.");
  1192.                     return (2);
  1193.                 }
  1194.                 else
  1195.                 {
  1196.                     debug (("Cache entry already queued\n"));
  1197.                     errno = 0;
  1198.                     ErrToReq (Req, 203, NULL, "Your request is already queued.<BR>\n"
  1199.                           "You will get the document next time you are online.<BR>\n"
  1200.                           "There is no expired cache entry for this document.");
  1201.                     LastCache = c;
  1202.                     return (2);
  1203.                 }
  1204.                 }
  1205.                 else
  1206.                 {
  1207.                 debug (("unqueueing and getting Cache entry\n"));
  1208.                 c->Flags   = 0;
  1209.                 c->File[0] = '_';
  1210.                 LastCache  = c;
  1211.                 goto HaveAlreadyCache;              /* sorry... */
  1212.                 }
  1213.             }
  1214.             else
  1215.             {
  1216.                 debug (("Second request while getting URL\n"));
  1217.                 ErrToReq (Req, 204, NULL, "The same request was sent from another connection.\n"
  1218.                       "May be you requested a queued URL which is just being received.<BR>\n"
  1219.                       "Please try again in a few moments.");
  1220.                 LastCache = c;
  1221.                 return (2);
  1222.             }
  1223.             }
  1224.         }
  1225.         }
  1226.  
  1227.     if (! (Known || ProxyProxy || OffLine))      /* not cached, unknown and online without proxyproxy... don't know where to get it */
  1228.     {
  1229.     errno = 0;
  1230.     ErrToReq (Req, 404, "Unknown host", "You tried to get a document from an unknown host. Please check the URL.");
  1231.     debug (("#%02d: unknown host: URL '%s'\n", Req-Requests, Url));
  1232.     return (2);
  1233.     }
  1234.  
  1235.  
  1236.     if (! (c = GetFreeCacheSlot ()) )            /* not in cache so far */
  1237.     {
  1238.     if (OffLine)
  1239.     {
  1240.         ErrToReq (Req, 500, "Cache table full", "I'm sorry, but there's no memory left for the cache table.<BR>"
  1241.               "It is not possible to queue up your request.");
  1242.         return (2);
  1243.     }
  1244.     else
  1245.     {
  1246.         fprintf (LogStream, "Cache table full\n");
  1247.         debug (("Cache table full\n"));
  1248.         return (0);                          /* no more free entries - just proxy it */
  1249.     }
  1250.     }
  1251.  
  1252.     if (OffLine)
  1253.     {
  1254.     ErrToReq (Req, 203, NULL, "Your new request is queued.<BR>\n"
  1255.           "You will get the document next time you are online.");
  1256.     debug (("Request queued.\n"));
  1257.     strcpy (c->Url, Url);
  1258.     c->Flags = CACHE_QUEUED;
  1259.     LastCache = c;
  1260.     SaveCacheUrl (c);
  1261.     c->File[0] = '@';
  1262.     return (2);
  1263.     }
  1264.  
  1265. HaveAlreadyCache:
  1266.  
  1267.     if (! (Req->Stream = fopen (c->File, "w")) )
  1268.     {
  1269.     fprintf (LogStream, "cannot open new cache file '%s': %s\n", c->File, strerror (errno));
  1270.     debug (("cannot open new cache file '%s': %s\n", c->File, strerror (errno)));
  1271.     c->File[0] = '\0';
  1272.     RemCacheEntry (c);
  1273.     return (0);                              /* just proxy it */
  1274.     }
  1275.  
  1276.     debug (("New cache file '%s'\n", c->File));
  1277.     strcpy (c->Url, Url);                        /* new cache entry */
  1278.     c->Flags = 0;
  1279.     Req->Cache = LastCache = c;
  1280.     return (0);
  1281. }
  1282.  
  1283.  
  1284. /*)) */
  1285. /*(( "GetCacheData ()" */
  1286.  
  1287. /* Fill request data space with data from cache
  1288.  * when a read error occures, no data will be received and ServWrite()
  1289.  * will automagically terminate the socket. */
  1290.  
  1291. void GetCacheData (request_t *Req)
  1292. {
  1293.     int Bytes;
  1294.  
  1295.     assert (Req->DataRecv < MAX_DATABUFFER);
  1296.     assert (Req->Stream);
  1297.  
  1298.     debug (("Getting more cache data - Url done: %s\n", Req->Flags & REQ_REQDONE ? "yes" : "no"));
  1299.  
  1300.     if ( (Bytes = fread (& Req->DataBuffer [Req->DataRecv], sizeof (char),
  1301.             MAX_DATABUFFER - Req->DataRecv, Req->Stream)) > 0)
  1302.     Req->DataRecv += Bytes;
  1303.     else
  1304.     if (! feof (Req->Stream))
  1305.     {
  1306.     fprintf (LogStream, "read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno));
  1307.     debug (("read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno)));
  1308.     }
  1309.     debug (("Read %d bytes\n", Bytes));
  1310. }
  1311.  
  1312.  
  1313. /*)) */
  1314. /*(( "BuildFdSets ()" */
  1315.  
  1316. /* Build fdsets (outstanding reads and writes) */
  1317.  
  1318. void BuildFdSets (fd_set *ReadSet, fd_set *WriteSet)
  1319. {
  1320.     int i;
  1321.     request_t *Req;
  1322.  
  1323.     FD_ZERO (ReadSet);
  1324.     FD_ZERO (WriteSet);
  1325.  
  1326.     debug (("Build: "));
  1327.     assert (RequestsFree >= 0);
  1328.     if (RequestsFree)
  1329.     {
  1330.     debug (("Server %d", (int) ServerSocket));
  1331.     FD_SET  (ServerSocket, ReadSet);
  1332.     }
  1333.  
  1334.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  1335.     {
  1336.     if (Req->Flags & REQ_REQSOCKET)
  1337.     {
  1338.         assert (Req->ReqSocket != 0);
  1339.         if (Req->ReqRecv < MAX_REQBUFFER-1)
  1340.         {
  1341.         debug ((", Read Req %d", Req-Requests));
  1342.         FD_SET (Req->ReqSocket, ReadSet);
  1343.         }
  1344.         if (Req->DataRecv > Req->DataSent)
  1345.         {
  1346.         debug ((", Write Req %d", Req-Requests));
  1347.         FD_SET (Req->ReqSocket, WriteSet);
  1348.         }
  1349.     }
  1350.     if (Req->Flags & REQ_CONNSOCKET)
  1351.     {
  1352.         assert (Req->ConnSocket != 0);
  1353.         if (Req->ReqRecv > Req->ReqSent || Req->UrlRecv > Req->UrlSent) /* Waiting for connect or sending request */
  1354.         {
  1355.         debug ((", Write Con %d", Req-Requests));
  1356.         FD_SET (Req->ConnSocket, WriteSet);
  1357.         }
  1358.         if (Req->DataRecv < MAX_DATABUFFER)                /* Transfer Data */
  1359.         {
  1360.         debug ((", Read Con %d", Req-Requests));
  1361.         FD_SET (Req->ConnSocket, ReadSet);
  1362.         }
  1363.     }
  1364.     }
  1365.     debug (("\n"));
  1366. }
  1367.  
  1368.  
  1369. /*)) */
  1370. /*(( "CreateUrlRequest()/CheckUrl()" */
  1371.  
  1372. /* Create Url request according to specification */
  1373. /* Returns the length of the request. */
  1374. /* Prot and Host may be NULL, in that case Name is to be asumed to be a full URL.
  1375.  * No checks for Proxyproxy mode are done in this case. */
  1376.  
  1377. size_t CreateUrlRequest (const char *Method, const char *Prot, const char *Host, int Port, const char *Name, char *ReqSpace, int HttpOne)
  1378. {
  1379.     char Buffer [MAX_URLBUFFER];
  1380.     char *p, *DokPrint;
  1381.  
  1382.     if (Prot && Host)
  1383.     {
  1384.     if (ProxyProxy)
  1385.     {
  1386.         if (Port > -1)
  1387.         sprintf (ReqSpace, "%s %s://%s:%d/", Method, Prot, Host, Port);
  1388.         else
  1389.         sprintf (ReqSpace, "%s %s://%s/", Method, Prot, Host);
  1390.     }
  1391.     else
  1392.         sprintf (ReqSpace, "%s /", Method);
  1393.     }
  1394.     else
  1395.     sprintf (ReqSpace, "%s ", Method);
  1396.  
  1397.     DokPrint = ReqSpace + strlen (ReqSpace);
  1398.  
  1399.     for (p = Buffer; *Name; )
  1400.     if (isvalidhttp (*Name))
  1401.         *p++ = *Name++;
  1402.     else
  1403.     {
  1404.         sprintf (p, "%%%02x", *Name++);
  1405.         p += 3;
  1406.     }
  1407.     *p = '\0';
  1408.  
  1409.     if (HttpOne)
  1410.     sprintf (DokPrint, "%s HTTP/1.0\r\n", Buffer);
  1411.     else
  1412.     sprintf (DokPrint, "%s\r\n", Buffer);
  1413.     debug (("Request '%s', len %d created.\n", ReqSpace, strlen (ReqSpace)));
  1414.     return (strlen (ReqSpace));
  1415. }
  1416.  
  1417.  
  1418. /* Check whether the URL is read completely. Sets REQ_REQDONE accordingly.
  1419.  * Attention! This routine relys on the fact, that ScanUrl was called already!
  1420.  * That means especially, that the first line was already read completely. */
  1421. /* The routine does not check for Url termination on Buffer boundaries. So
  1422.  * the ReqBuffer has to be shifted always (four bytes remaining always) and
  1423.  * never be cleared at all. */
  1424.  
  1425. void CheckUrl (request_t *Req)
  1426. {
  1427.     debug (("Checking Url\n"));
  1428.     if (Req->Flags & REQ_HTTP1X0)
  1429.     {
  1430.     if (strstr (Req->ReqBuffer, "\n\n") || strstr (Req->ReqBuffer, "\r\r") ||
  1431.         strstr (Req->ReqBuffer, "\n\r\n\r") || strstr (Req->ReqBuffer, "\r\n\r\n"))
  1432.         Req->Flags |= REQ_REQDONE;
  1433.     }
  1434.     else
  1435.     Req->Flags |= REQ_REQDONE;
  1436. #ifdef DEBUG
  1437.    if (Req->Flags & REQ_REQDONE)
  1438.     debug (("http request complete\n"));
  1439. #endif
  1440. }
  1441.  
  1442.  
  1443. /*)) */
  1444. /*(( "ScanUrl ()" */
  1445.  
  1446. /* Scan the URL and divide it into several parts. Understands http/0.9
  1447.  * and http/1.0 versions right now. Sets REQ_REQDONE when URL is complete. */
  1448. /* Req->UrlBuffer is valid after calling this routine and REQ_URLDONE set. */
  1449. /* Returns -1 in case of failure, 0 on correct termination or found cache,
  1450.  * 1: found cache slot, FileD is open, 2: failure, cache contains error */
  1451. /* Port and Address are value-return arguments. */
  1452.  
  1453. int ScanUrl (request_t *Req, struct in_addr *Address, int *Port)
  1454. {
  1455.     struct hostent *HostEnt;
  1456.     struct in_addr In;
  1457.     char BufMETH [12];
  1458.     char BufPROT [16];
  1459.     char BufDOK [MAX_URLSAVE + 256];                                       /* a critical array... but that's enough */
  1460.     char BufURL [128];
  1461.     char BufVERS [8];
  1462.     int  CharsRead, CharsRead2, FoundHost = 0, i;
  1463.     char *DokPtr, *PortPtr;
  1464.     char *Host, *ObjectName;
  1465.  
  1466.     if (sscanf (Req->ReqBuffer, "%11s %15[a-zA-Z0-9]%n", BufMETH, BufPROT, &CharsRead) < 2)
  1467.     return (-1);
  1468.     debug (("METHOD '%s', PROT '%s'\n", BufMETH, BufPROT));
  1469.     if (CharsRead >= Req->ReqRecv)
  1470.     return (-1);
  1471.     if (OffLine && strcasecmp (BufMETH, "get") != 0)                       /* no get command */
  1472.     return (-1);
  1473.     if (! ProxyProxy)
  1474.     if (strcasecmp (BufPROT, "http") != 0)                             /* we only support http urls so far */
  1475.         return (-1);
  1476.     if (strncmp (& Req->ReqBuffer [CharsRead], "://", 3) != 0)
  1477.     return (-1);
  1478.     if (sscanf (& Req->ReqBuffer [CharsRead+3], "%126s%n", BufURL, &CharsRead2) < 1)
  1479.     return (-1);
  1480.     debug (("URL '%s'\n", BufURL));
  1481.     CharsRead += 3 + CharsRead2;
  1482.  
  1483.     if (! (DokPtr = strchr (BufURL, '/')) )                                /* no host... */
  1484.     return (-1);
  1485.     *DokPtr++ = 0;
  1486.     ObjectName = DokPtr;
  1487.  
  1488.     if (strcasecmp (BufPROT, "http") != 0)
  1489.     *Port = -1;
  1490.     else
  1491.     *Port = StdHttpPort;
  1492.     if ( (PortPtr = strchr (BufURL, ':')) )
  1493.     {
  1494.     *PortPtr = 0;
  1495.     *Port = atoi (PortPtr + 1);
  1496.     }
  1497.  
  1498.     Host = BufURL;
  1499.     if (! (ProxyProxy || OffLine))
  1500.     {
  1501.     if ( (In.s_addr = inet_addr (BufURL)) != -1)
  1502.         FoundHost = 1;
  1503.     else if ( (HostEnt = gethostbyname (BufURL)) ) 
  1504.     {
  1505.         debug (("gethostbyname () succeded\n"));
  1506.         memmove (&In, HostEnt->h_addr, sizeof (struct in_addr));
  1507.         FoundHost = 1;
  1508.     }
  1509.     }
  1510.     else
  1511.     FoundHost = 1;           /* we do know our proxy host */
  1512.  
  1513.     *Address = In;
  1514.  
  1515.     BufVERS [2] = 0;                                                       /* BufVERS is missused here... */
  1516.     if (*Port > -1)
  1517.     sprintf (BufDOK, "%s://%s:%d/", BufPROT, Host, *Port);
  1518.     else
  1519.     sprintf (BufDOK, "%s://%s/", BufPROT, Host);
  1520.     for (PortPtr = BufDOK + strlen (BufDOK); *DokPtr; DokPtr++, PortPtr++)
  1521.     {
  1522.     if (*DokPtr == '%')
  1523.     {
  1524.         if (! (BufVERS [0] = *++DokPtr) )
  1525.         break;
  1526.         if (! (BufVERS [1] = *++DokPtr) )
  1527.         break;
  1528.         if ( (i = strtol (BufVERS, NULL, 0x10)) )
  1529.         *PortPtr = i;
  1530.         else
  1531.         *PortPtr = '?';
  1532.     }
  1533.     else
  1534.         *PortPtr = *DokPtr;
  1535.     }
  1536.     *PortPtr = 0;
  1537.  
  1538.     debug (("Proto '%s', Host '%s', Port %d, Dok '%s'\n", BufPROT, Host, *Port, BufDOK));
  1539.  
  1540.     if (Req->ReqBuffer [CharsRead] == ' ' && CharsRead < Req->ReqRecv)
  1541.     if (sscanf (& Req->ReqBuffer [CharsRead], " %5s", BufVERS) == 1)
  1542.         if (strcasecmp (BufVERS, "http/") == 0)
  1543.         {
  1544.         debug (("got HTTP/1.0 or greater request\n"));
  1545.         Req->Flags |= REQ_HTTP1X0;
  1546.         }
  1547.  
  1548.     CheckUrl (Req);
  1549.     if (strlen (BufDOK) > MAX_URLSAVE-1)                     /* That one cannot be cached */
  1550.     {
  1551.     fprintf (LogStream, "ReqBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK);
  1552.     debug (("ReqBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK));
  1553.     return (0);
  1554.     }
  1555.  
  1556.     /* create UrlBuffer entry */
  1557.     assert (Req->UrlSent == 0);
  1558.     Req->UrlRecv = CreateUrlRequest (BufMETH, BufPROT, BufURL, *Port, ObjectName, Req->UrlBuffer, Req->Flags & REQ_HTTP1X0);
  1559.     PortPtr = strchr (& Req->ReqBuffer [CharsRead], '\n');   /* PortPtr und DokPtr are missused here */
  1560.     DokPtr  = strchr (& Req->ReqBuffer [CharsRead], '\r');
  1561.     assert (DokPtr == NULL || PortPtr == NULL || PortPtr == DokPtr + 1 || DokPtr == PortPtr + 1);
  1562.     if (DokPtr == PortPtr + 1 || PortPtr == NULL)
  1563.     PortPtr = DokPtr;
  1564.     assert (PortPtr != NULL);
  1565.  
  1566.     Req->ReqSent = PortPtr - Req->ReqBuffer + 1;
  1567.     Req->Flags  |= REQ_URLDONE;
  1568.  
  1569.     if (strcasecmp (BufMETH, "get") != 0)                                  /* only get's will be cached */
  1570.     {
  1571.     debug (("no get method - no caching\n"));
  1572.     return (0);
  1573.     }
  1574.  
  1575.     return (ScanCache (Req, BufDOK, FoundHost));
  1576. }
  1577.  
  1578.  
  1579. /*)) */
  1580. /*(( "DeleteRequest/Connect ()" */
  1581.  
  1582. /* Close the connection socket only. When a request socket is still open
  1583.  * and no data is in the cache, close all.
  1584.  * Delete cache entry in case of an error. Check the answer whether it is
  1585.  * an error message or a regular answer. Errors should not be cached! */
  1586. /* When Flags == 0 nothing is done at all */
  1587.  
  1588. void DeleteConnect (request_t *Req, int ok)
  1589. {
  1590. /*    cache_t tmp;*/
  1591.  
  1592.     debug (("DeleteCon %d\n", Req-Requests));
  1593.     if (! Req->Flags)
  1594.     return;
  1595.     if (Req->Flags & REQ_CONNSOCKET)
  1596.     {
  1597.     CloseSocket (Req->ConnSocket);
  1598.     Req->Flags &= ~REQ_CONNSOCKET;
  1599.     if (Req->Stream)
  1600.         fclose (Req->Stream);
  1601.     Req->Stream = NULL;
  1602.     if (ok)
  1603.     {
  1604.         if (Req->Cache && ! (Req->Cache->Flags & CACHE_QUEUED)) /* we've written into a cache file */
  1605.         {
  1606.         SaveCacheUrl (Req->Cache);
  1607.         Req->Cache = NULL;
  1608.         }
  1609.     }
  1610.     else if (Req->Cache)                                        /* not ok - a error occured */
  1611.     {
  1612.         fprintf (LogStream, "error while receiving URL '%s' - removing\n", Req->Cache->Url);
  1613.         debug (("error while receiving URL '%s' - removing\n", Req->Cache->Url));
  1614.         Req->Cache->Url[0] = '\0';
  1615.         RemCacheEntry (Req->Cache);
  1616.         Req->Cache = NULL;
  1617.     }
  1618.     }
  1619.     if (Req->Flags & REQ_REQSOCKET)
  1620.     {
  1621.     if (Req->DataRecv <= Req->DataSent && (Req->Flags & REQ_REQDONE))
  1622.     {
  1623.         CloseSocket (Req->ReqSocket);       /* All done. Hugh! */
  1624.         Req->Flags = 0;
  1625.     }
  1626.     else
  1627.         Req->Flags |= REQ_DONE;
  1628.     }
  1629.     else
  1630.     Req->Flags = 0;
  1631.     if (! Req->Flags)
  1632.     RequestsFree++;
  1633. }
  1634.  
  1635.  
  1636. /* Close the request socket only. When a connection socket is already up
  1637.  * (and enough data is already read) continue filling up the cache. When
  1638.  * everything is done, close all. The URL should be checked, too.
  1639.  * When it is not complete, all connections should be terminated
  1640.  * on CacheUnreadRequests false. */
  1641. /* Don't call DeleteConnect when Flags == 0... */
  1642.  
  1643. void DeleteRequest (request_t *Req, int ok)
  1644. {
  1645.     debug (("DeleteReq %d\n", Req-Requests));
  1646.     if (Req->Flags & REQ_REQSOCKET)
  1647.     {
  1648.     CloseSocket (Req->ReqSocket);
  1649.     Req->Flags &= ~REQ_REQSOCKET;
  1650.     }
  1651.  
  1652.     if (Req->Flags & REQ_DONE)                  /* Maybe we're getting data from the cache */
  1653.     {
  1654.     if (Req->Stream)
  1655.         fclose (Req->Stream);
  1656.     Req->Stream = NULL;
  1657.     DeleteConnect (Req, ok);
  1658.     Req->Flags  = 0;                        /* All done. Hugh! */
  1659.     debug (("All done - deleterequest\n"));
  1660.     }
  1661.     else if (Req->Flags & REQ_REQDONE)
  1662.     {
  1663.     if (! (Req->Flags & REQ_CONNSOCKET))    /* Aborting cache sending */
  1664.         DeleteConnect (Req, FALSE);
  1665.     else if (CacheUnreadRequests)
  1666.         Req->DataSent = Req->DataRecv = 0;  /* Continue getting data into the cache */
  1667.     }
  1668.     else
  1669.     {
  1670.     Req->Flags |= REQ_DONE;
  1671.     DeleteConnect (Req, FALSE);             /* No request known so far... */
  1672.     }
  1673. }
  1674.  
  1675.  
  1676. /*)) */
  1677. /*(( "ServConnect ()" */
  1678.  
  1679. /* The first line of the URL is already there, so we can connect to the
  1680.  * remote host. */
  1681.  
  1682. void ServConnect (request_t *Req)
  1683. {
  1684.     struct sockaddr_in SockIn;
  1685.     struct sockaddr_in *SockPtr = &SockIn;
  1686.     ioctl_t on = 1;
  1687.     int  Port;
  1688.  
  1689.     debug (("ServCon for Req %d; Flags %x\n", Req-Requests, Req->Flags));
  1690.  
  1691.     if (Req->Flags & REQ_REQSOCKET)
  1692.     {
  1693.     if (strchr (Req->ReqBuffer, '\n') == 0 &&
  1694.         strchr (Req->ReqBuffer, '\r') == 0)         /* first line of URL is not there... */
  1695.         return;
  1696.  
  1697.     if (Req->Flags & REQ_URLDONE)  /* already set up */
  1698.     {
  1699.         CheckUrl (Req);
  1700.         return;
  1701.     }
  1702.     }
  1703.  
  1704.     memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
  1705.  
  1706.     switch (ScanUrl (Req, &SockIn.sin_addr, &Port)) {
  1707.     case 0:                                   /* need to get document from remote host */
  1708.     if (Req->Cache)
  1709.     {
  1710.         fprintf (LogStream, "#%02d: getting URL '%s'\n", Req-Requests, Req->Cache->Url);
  1711.         debug (("#%02d: getting URL '%s'\n", Req-Requests, Req->Cache->Url));
  1712.     }
  1713.     else
  1714.     {
  1715.         fprintf (LogStream, "#%02d: serving uncacheable request '%s'\n",
  1716.              Req-Requests, Req->ReqBuffer);
  1717.         debug  (("#%02d: serving uncacheable request '%s'\n",
  1718.              Req-Requests, Req->ReqBuffer));
  1719.     }
  1720.     break;
  1721.  
  1722.     case 1:                                   /* already cached - no need to connect to remote host */
  1723.     assert (Req->Cache != NULL);
  1724.     fprintf (LogStream, "#%02d: sending URL '%s' from cache '%s'\n",
  1725.          Req-Requests, Req->Cache->Url, Req->Cache->File);
  1726.     debug  (("#%02d: sending URL '%s' from cache '%s'\n",
  1727.          Req-Requests, Req->Cache->Url, Req->Cache->File));
  1728.     GetCacheData (Req);
  1729.     return;
  1730.  
  1731.     case 2:                                   /* send error message from buffer */
  1732.     return;
  1733.  
  1734.     default:
  1735.     {
  1736.         fprintf (LogStream, "#%02d: invalid request\n", Req-Requests);
  1737.         debug (("#%02d: invalid request\n", Req-Requests));
  1738.         Req->Flags |= REQ_DONE | REQ_REQDONE;
  1739.         errno = 0;
  1740.         ErrToReq (Req, 500, "Invalid Request", "Your Request is not a valid URL.<BR>\n"
  1741.               "Please check it and try again.");
  1742.         return;
  1743.     }
  1744.     }
  1745.  
  1746.     if (OffLine)
  1747.     {
  1748.     errno = 0;
  1749.     ErrToReq (Req, 500, "Unable to serv", "It is not possible to connect to a remote host in offline mode.<BR>\n"
  1750.           "Either the requested URL is to long to be queued or an internal error has occured. When the\n"
  1751.           "URL is rather short, please contact the author of" PRG_NAME);
  1752.     return;
  1753.     }
  1754.  
  1755.     if (ProxyProxy)
  1756.     SockPtr = &ProxyProxyIn;
  1757.     else
  1758.     {
  1759.     SockIn.sin_family     = AF_INET;
  1760.     SockIn.sin_port       = htons (Port);
  1761.     }
  1762.  
  1763.  
  1764.     if ( (Req->ConnSocket = socket (AF_INET, SOCK_STREAM, 0)) < 0)                 /* open connection */
  1765.     {
  1766.     syslog (LOG_ERR, "%s: socket () failed", PrgName);
  1767.     DeleteRequest (Req, FALSE);
  1768.     return;
  1769.     }
  1770.  
  1771.     Req->Flags |= REQ_CONNSOCKET;
  1772.  
  1773. #ifdef FIOASYNC
  1774.     if (ioctl (Req->ConnSocket, FIOASYNC, (caddr_t) &on) < 0)
  1775.     {
  1776.     syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
  1777.     DeleteRequest (Req, FALSE);
  1778.     return;
  1779.     }
  1780. #endif
  1781. #ifdef FIONBIO
  1782.     if (ioctl (Req->ConnSocket, FIONBIO, (caddr_t) &on) < 0)
  1783.     {
  1784.     syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
  1785.     DeleteRequest (Req, FALSE);
  1786.     return;
  1787.     }
  1788. #endif
  1789.  
  1790.     if (connect (Req->ConnSocket, (struct sockaddr *) SockPtr, sizeof (struct sockaddr_in)) < 0)
  1791.     {
  1792.     if (errno != EINPROGRESS)
  1793.     {
  1794.         if (ProxyProxy)
  1795.         ErrToReq (Req, 500, "Proxyproxy Host Unreachable",
  1796.               "The host you specified with the 'proxy' option can't be contacted.<BR>\n"
  1797.               "Please wait until the proxy is up again or start " PRG_NAME " in offline mode.");
  1798.         else
  1799.         ErrToReq (Req, 500, "Host Unreachable",
  1800.               "The specified host can't be contacted.<BR>\n"
  1801.               "Please wait until it is up again or try any other URLs.");
  1802.         return;
  1803.     }
  1804.     }
  1805.     debug (("ServCon -> Conn %d\n", (int) Req->ConnSocket));
  1806. }
  1807.  
  1808.  
  1809. /*)) */
  1810. /*(( "ServServer ()" */
  1811.  
  1812. /* Serv the server port. Accept new connections, set up request database */
  1813.  
  1814. void ServServer (void)
  1815. {
  1816.     struct sockaddr_in PeerIn;
  1817.     len_t  Len = sizeof (struct sockaddr_in);
  1818.     request_t *Req = Requests;
  1819.     ioctl_t on = 1;
  1820.  
  1821.     assert (RequestsFree > 0);
  1822.  
  1823.     while (Req->Flags)                        /* any Flags set -> occupied */
  1824.     Req++;
  1825.     debug (("accept Req %d\n", Req-Requests));
  1826.     assert (Req - Requests < MaxRequests);
  1827.  
  1828.     if ( (Req->ReqSocket = accept (ServerSocket, (struct sockaddr *) &PeerIn, &Len)) < 0)
  1829.     {
  1830.     syslog (LOG_ERR, "%s: accept() failed: %s", PrgName, strerror (errno));
  1831.     return;
  1832.     }
  1833.  
  1834.     fprintf (LogStream, "#%02d: new request from %s:%d\n",
  1835.          Req-Requests, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port);
  1836.     debug  (("#%02d: new request from %s:%d\n",
  1837.          Req-Requests, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port));
  1838.  
  1839.     Req->Flags |= REQ_REQSOCKET;
  1840.     Req->ReqBuffer[0] = Req->UrlBuffer [0] = Req->DataBuffer [0] = '\0';
  1841.     Req->ReqSent = Req->ReqRecv = Req->UrlSent = Req->UrlRecv =
  1842.            Req->DataSent = Req->DataRecv = 0;
  1843.     Req->Cache = NULL;
  1844.     Req->Stream = NULL;
  1845.     RequestsFree--;
  1846.  
  1847. #ifdef FIOASYNC
  1848.     if (ioctl (Req->ReqSocket, FIOASYNC, (caddr_t) &on) < 0)
  1849.     {
  1850.     syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
  1851.     DeleteRequest (Req, FALSE);
  1852.     return;
  1853.     }
  1854. #endif
  1855. #ifdef FIONBIO
  1856.     if (ioctl (Req->ReqSocket, FIONBIO, (caddr_t) &on) < 0)
  1857.     {
  1858.     syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
  1859.     DeleteRequest (Req, FALSE);
  1860.     return;
  1861.     }
  1862. #endif
  1863. }
  1864.  
  1865.  
  1866. /*)) */
  1867. /*(( "ServRead ()" */
  1868.  
  1869. /* Server for all reads */
  1870.  
  1871. void ServRead (fd_set *ReadSet)
  1872. {
  1873.     int i, Bytes;
  1874.     register request_t *Req;
  1875.  
  1876.     if (FD_ISSET (ServerSocket, ReadSet))
  1877.     ServServer ();
  1878.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  1879.     {
  1880.     if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, ReadSet))
  1881.     {
  1882.         assert (Req->ReqRecv < MAX_REQBUFFER-1);
  1883.         if ( (Bytes = recv (Req->ReqSocket, & Req->ReqBuffer [Req->ReqRecv],
  1884.                 (long) MAX_REQBUFFER-1 - Req->ReqRecv, 0)) < 0)
  1885.         {
  1886.         fprintf (LogStream, "#%02d: on receiving URL: %s\n", Req-Requests, strerror (errno));
  1887.         debug (("#%02d: on receiving URL: %s\n", Req-Requests, strerror (errno)));
  1888.         DeleteRequest (Req, FALSE);
  1889.         }
  1890.         else
  1891.         {
  1892.         Req->ReqRecv += Bytes;
  1893.         Req->ReqBuffer [Req->ReqRecv] = '\0';        /* needed for strchr() */
  1894.  
  1895.         debug (("Read %d bytes from Req %d to 0x%x containing:\n'%s'\n", Bytes,
  1896.             Req-Requests, Req->ReqRecv-Bytes, & Req->ReqBuffer [Req->ReqRecv - Bytes]));
  1897. #if defined (DEBUG) && ! defined (_M68020)
  1898.         if ((Req->ReqRecv - Bytes) & 0x0001)
  1899.             debug (("(odd address...\n"));
  1900.         else
  1901. #endif
  1902.         debug (("= 0x %08lx %08lx %08lx %08lx\n", * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes),
  1903.             * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 4), * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 8),
  1904.             * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 12)));
  1905.  
  1906.         if (Bytes == 0)                              /* request socket gone away */
  1907.         {
  1908.             DeleteRequest (Req, TRUE);
  1909.             continue;
  1910.         }
  1911.         ServConnect (Req);                           /* Check if anything can be done already */
  1912.         if ((Req->Flags & REQ_DONE) && (Req->Flags & REQ_REQDONE) && (Req->DataRecv == 0))
  1913.         {
  1914.             DeleteRequest (Req, TRUE);               /* all done, another time */
  1915.             continue;
  1916.         }
  1917.         if ((Req->ReqRecv == MAX_REQBUFFER-1) && ! (Req->Flags & REQ_CONNSOCKET))
  1918.         {                                            /* buffer full and no connection yet */
  1919.             if (Req->Flags & REQ_URLDONE)
  1920.             Req->ReqRecv = Req->ReqSent = 0;     /* Skip remaining data (we won't send it...) */
  1921.             else
  1922.             {
  1923.             fprintf (LogStream, "#%02d: URL buffer overflow\n", Req-Requests);
  1924.             debug (("#%02d: URL buffer overflow\n", Req-Requests));
  1925.             DeleteRequest (Req, FALSE);
  1926.             }
  1927.         }
  1928.         }
  1929.     }
  1930.     if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, ReadSet))
  1931.     {
  1932.         assert (Req->DataRecv < MAX_DATABUFFER);
  1933.         if ( (Bytes = recv (Req->ConnSocket, & Req->DataBuffer [Req->DataRecv],
  1934.                 (long) MAX_DATABUFFER - Req->DataRecv, 0)) < 0)
  1935.         {
  1936.         if (ProxyProxy)
  1937.             ErrToReq (Req, 500, "Proxyproxy Host Unreachable / read() failed",
  1938.                   "The host you specified with the 'proxy' option can't be contacted.<BR>\n"
  1939.                   "Please wait until the proxy is up again or start " PRG_NAME " in offline mode.");
  1940.         else
  1941.             ErrToReq (Req, 500, "Host Unreachable / read() failed",
  1942.                   "The specified host can't be contacted.<BR>\n"
  1943.                   "Please wait until it is up again or try any other URLs.");
  1944.         DeleteConnect (Req, FALSE);
  1945.         }
  1946.         else
  1947.         {
  1948.         debug (("Read %d bytes from Con %d\n", Bytes, Req-Requests));
  1949.         if (Bytes == 0)                               /* We're done */
  1950.         {
  1951.             DeleteConnect (Req, TRUE);
  1952.             continue;
  1953.         }
  1954.         else if (Req->Stream)
  1955.             fwrite (& Req->DataBuffer [Req->DataRecv], sizeof (char),      /* no error checking... */
  1956.                 Bytes, Req->Stream);
  1957.         if (Req->Flags & REQ_REQSOCKET)
  1958.             Req->DataRecv += Bytes;                   /* else: only save cache data */
  1959.         }
  1960.     }
  1961.     }
  1962. }
  1963.  
  1964.  
  1965. /*)) */
  1966. /*(( "ServWrite ()" */
  1967.  
  1968. /* Server for all writes */
  1969.  
  1970. void ServWrite (fd_set *WriteSet)
  1971. {
  1972.     int i, Bytes;
  1973.     register request_t *Req;
  1974.  
  1975.     for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
  1976.     {
  1977.     if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, WriteSet))
  1978.     {
  1979.         assert (Req->DataRecv > Req->DataSent);
  1980.         if ( (Bytes = send (Req->ReqSocket, & Req->DataBuffer [Req->DataSent],
  1981.                 (long) Req->DataRecv - Req->DataSent, 0)) < 0)
  1982.         {
  1983.         fprintf (LogStream, "#%02d: on sending data: %s\n", Req-Requests, strerror (errno));
  1984.         debug (("#%02d: on sending data: %s\n", Req-Requests, strerror (errno)));
  1985.         DeleteRequest (Req, FALSE);
  1986.         continue;
  1987.         }
  1988.         else
  1989.         {
  1990.         debug (("Wrote %d bytes to Req %d\n", Bytes, Req-Requests));
  1991.         Req->DataSent += Bytes;
  1992.         assert (Req->DataRecv >= Req->DataSent);
  1993.         if (Bytes == 0)                            /* request socket is unable to receive data */
  1994.         {
  1995.             fprintf (LogStream, "#%02d: request socket unable to receive data\n", Req-Requests);
  1996.             debug (("#%02d: request socket unable to receive data\n", Req-Requests));
  1997.             DeleteRequest (Req, FALSE);
  1998.             continue;
  1999.         }
  2000.  
  2001.         if (Req->DataSent == Req->DataRecv)        /* clear / shift data buffer */
  2002.             Req->DataSent = Req->DataRecv = 0;
  2003.         else if (Req->DataSent > SHIFT_DATABUFFER)
  2004.         {
  2005.             debug (("shifting databuffer size %d by %d bytes\n", Req->DataRecv - Req->DataSent, Req->DataSent));
  2006.             memmove (Req->DataBuffer, & Req->DataBuffer [Req->DataSent],
  2007.                  Req->DataRecv - Req->DataSent);
  2008.             Req->DataRecv -= Req->DataSent;
  2009.             Req->DataSent = 0;
  2010.         }
  2011.         if (Req->DataRecv < MAX_DATABUFFER && (Req->Flags & REQ_DONE) && Req->Stream)
  2012.             GetCacheData (Req);
  2013.         if (Req->DataRecv == 0 && (Req->Flags & REQ_DONE) && (Req->Flags & REQ_REQDONE))   /* We're already done */
  2014.             DeleteRequest (Req, TRUE);
  2015.  
  2016.         }
  2017.     }
  2018.     if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, WriteSet))
  2019.     {
  2020.         if (Req->UrlRecv > Req->UrlSent)
  2021.         Bytes = send (Req->ConnSocket, &Req->UrlBuffer [Req->UrlSent],
  2022.                   (long) Req->UrlRecv - Req->UrlSent, 0);
  2023.         else
  2024.         {
  2025.         assert (Req->ReqRecv > Req->ReqSent);
  2026.         Bytes = send (Req->ConnSocket, & Req->ReqBuffer [Req->ReqSent],
  2027.                   (long) Req->ReqRecv - Req->ReqSent, 0);
  2028.         }
  2029.         if (Bytes < 0)
  2030.         {
  2031.         if (ProxyProxy)
  2032.         {
  2033.             fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", Req-Requests, strerror (errno));
  2034.             debug (("#%02d: proxyproxy host unreachable: %s\n", Req-Requests, strerror (errno)));
  2035.         }
  2036.         else
  2037.         {
  2038.             fprintf (LogStream, "#%02d: on sending url: %s\n", Req-Requests, strerror (errno));
  2039.             debug (("#%02d: on sending url: %s\n", Req-Requests, strerror (errno)));
  2040.         }
  2041.         DeleteConnect (Req, FALSE);
  2042.         }
  2043.         else
  2044.         {
  2045.         debug (("Wrote %d bytes to Conn %d from %s, 0x%x\n", Bytes, Req-Requests,
  2046.             Req->UrlRecv > Req->UrlSent ? "UrlBuffer" : "ReqBuffer", Req->UrlRecv > Req->UrlSent ? Req->UrlSent : Req->ReqSent));
  2047.  
  2048.         if (Req->UrlRecv > Req->UrlSent)
  2049.             Req->UrlSent += Bytes;
  2050.         else
  2051.             Req->ReqSent += Bytes;
  2052.         if (Bytes == 0)                               /* connection socket is unable to receive url */
  2053.         {
  2054.             fprintf (LogStream, "#%02d: connection socket unable to receive url\n", Req-Requests);
  2055.             debug (("#%02d: connection socket unable to receive url\n", Req-Requests));
  2056.             DeleteConnect (Req, FALSE);
  2057.             continue;
  2058.         }
  2059.  
  2060.         if (Req->ReqSent > SHIFT_REQBUFFER)
  2061.         {
  2062.             debug (("shifting reqbuffer size %d+4 by %d bytes\n", Req->ReqRecv - Req->ReqSent, Req->ReqSent-4));
  2063.             memmove (Req->ReqBuffer, & Req->ReqBuffer [Req->ReqSent - 4],
  2064.                  Req->ReqRecv - Req->ReqSent + 4);
  2065.             Req->ReqRecv -= Req->ReqSent - 4;
  2066.             Req->ReqSent = 4;
  2067.         }
  2068.         }
  2069.     }
  2070.     }
  2071. }
  2072.  
  2073.  
  2074. /*)) */
  2075. /*(( "CacheInUse()/RequestQueued()" */
  2076.  
  2077. /* Check wheather a cache entry is used in any other request */
  2078.  
  2079. request_t *CacheInUse (cache_t *c)
  2080. {
  2081.     request_t *Req = Requests;
  2082.     int i;
  2083.  
  2084.     for (i = 0; i < MaxRequests; i++, Req++)
  2085.     if (Req->Cache == c)
  2086.         return (Req);
  2087.     return (NULL);
  2088. }
  2089.  
  2090.  
  2091. /* Initiate contact to remote host for a queued request */
  2092.  
  2093. void RequestQueued (cache_t *c)
  2094. {
  2095.     request_t *Req = Requests;
  2096.     int     Len;
  2097.  
  2098.     assert (MAX_REQBUFFER >= 4 * MAX_URLSAVE);  /* to be on the save side */
  2099.     fprintf (LogStream, "initiating request for Url '%s'\n", c->Url);
  2100.  
  2101.     while (Req->Flags)                        /* any Flags set -> occupied */
  2102.     Req++;
  2103.     assert (Req - Requests < MaxRequests);
  2104.  
  2105.     Req->Flags  = REQ_REQDONE;
  2106.     Req->ReqSent= Req->DataSent = Req->DataRecv = Req->UrlSent = Req->UrlRecv = 0;
  2107.     Req->Cache  = NULL;
  2108.     Req->Stream = NULL;
  2109.     RequestsFree--;
  2110.  
  2111.     Len = CreateUrlRequest ("GET", NULL, NULL, 0, c->Url, Req->ReqBuffer, TRUE);
  2112.     sprintf (& Req->ReqBuffer [Len], "User-Agent: %s\r\nAccept: */*\r\n\r\n", VERSHTTP);
  2113.     Req->ReqRecv = strlen (Req->ReqBuffer);
  2114.     debug (("initiating request '%s'\n", Req->ReqBuffer));
  2115.     if (c->File [0] == '@')
  2116.     c->Url [0] = '\0';
  2117.     RemCacheEntry (c);
  2118.     ServConnect (Req);
  2119. }
  2120.  
  2121.  
  2122. /*)) */
  2123. /*(( "CheckGetQueued()" */
  2124.  
  2125. /* Check number of pending requests and initiate getting of queued URLs */
  2126.  
  2127. void CheckGetQueued (void)
  2128. {
  2129.     static cachearray_t *a = NULL;
  2130.     static int Nr = 0;
  2131.     static int State = 1;
  2132.     cache_t *c;
  2133.  
  2134.     if (State == 3 || ! GetQueued)
  2135.     return;
  2136.     if (a == NULL)
  2137.     a = Caches;
  2138.  
  2139.     debug (("State %d\n", State));
  2140.  
  2141.     while (RequestsFree > MIN_REQUESTS)
  2142.     switch (State) {
  2143.     case 1:                         /* get all CACHE_QUEUED & ! CACHE_VALID */
  2144.         for (c = & a->Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
  2145.         if ((c->Flags & CACHE_QUEUED) && ! (c->Flags & CACHE_VALID))    /* get urls without expired caches first */
  2146.             if (! CacheInUse (c))             /* inUse caches won't be get at all... */
  2147.             {
  2148.             RequestQueued (c);
  2149.             break;
  2150.             }
  2151.         if (Nr < MAX_CACHES)
  2152.         continue;
  2153.         Nr = 0;
  2154.         if (a->Next)
  2155.         {
  2156.         a = a->Next;
  2157.         continue;
  2158.         }
  2159.  
  2160.         State = 2;
  2161.         a     = Caches;  /* no break! */
  2162.  
  2163.     case 2:
  2164.         for (c = & a->Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
  2165.         if (c->Flags & CACHE_QUEUED)
  2166.         {
  2167.             assert (c->Flags & CACHE_VALID);
  2168.             if (! CacheInUse (c))
  2169.             {
  2170.             RequestQueued (c);
  2171.             break;
  2172.             }
  2173.         }
  2174.         if (Nr < MAX_CACHES)
  2175.         continue;
  2176.         Nr = 0;
  2177.         if (a->Next)
  2178.         {
  2179.         a = a->Next;
  2180.         continue;
  2181.         }
  2182.         State = 3;
  2183.         return;
  2184.  
  2185.     default:
  2186.         assert (0);
  2187.     }
  2188. }
  2189.  
  2190.  
  2191. /*)) */
  2192. /*(( "DeleteInvalidCaches()" */
  2193.  
  2194. /* Shutdown: Close and delete all invalid cache entries. */
  2195.  
  2196. void DeleteInvalidCaches (void)
  2197. {
  2198.     cachearray_t *a;
  2199.     cache_t *c, Tmp, *cc;
  2200.     request_t *Req;
  2201.     int     i;
  2202.  
  2203.     for (a = Caches; a; a = a->Next)
  2204.     for (i = 0, c = a->Caches; i < MAX_CACHES; i++, c++)
  2205.         if (c->File[0] && (c->Flags & (CACHE_VALID | CACHE_QUEUED)) +0 == 0)      /* !!! */
  2206.         {
  2207.         Req = CacheInUse (c);
  2208.         if (Req)
  2209.         {
  2210.             if (Req->Stream)                      /* close cache stream */
  2211.             fclose (Req->Stream);
  2212.             Req->Stream = NULL;
  2213.             if (c->Url[0])
  2214.             {
  2215.             fprintf (LogStream, "removing and queueing URL '%s'\n", c->Url);
  2216.             SaveCacheUrl (c);
  2217.             assert (c->Url[0] != '\0');
  2218.             Tmp = *c;
  2219.             c->Url[0] = '\0';                 /* keep the Urlfile */
  2220.             RemCacheEntry (c);
  2221.             cc = GetFreeCacheSlot ();
  2222.             assert (cc != NULL);
  2223.             InitCacheSlot (cc, &Tmp, '@', '.');
  2224.             cc->Flags = CACHE_QUEUED;
  2225.             }
  2226.             else
  2227.             RemCacheEntry (c);
  2228.         }
  2229.         else
  2230.             debug (("File '%s', Url '%s', Flags 0x%x without Request!\n", c->File, c->Url, c->Flags));
  2231.         }
  2232. }
  2233.  
  2234.  
  2235. /*)) */
  2236. /*(( "main ()" */
  2237.  
  2238. /* The main routine */
  2239.  
  2240. void main (int argc, char **argv)
  2241. {
  2242.     fd_set ReadSet;
  2243.     fd_set WriteSet;
  2244.     char   *ProxyHost = NULL;
  2245.     char   *LogFile   = "." PRG_NAME "-log";
  2246.     int    ProxyPort;
  2247.  
  2248.     PrgName = *argv++;
  2249.  
  2250.     if (--argc == 1)
  2251.     if (**argv == '?')
  2252.     {
  2253.         fprintf (stderr, "Usage: %s [proxy PROXYHOST PROXYPORT] [port PORT] [cache DIR] [del SECONDS]\n"
  2254.              "[expire SECONDS] [reload SECONDS] [log FILE] [numreq NUMBER]\n"
  2255.              "[unread] [offline] [get]\n"
  2256.                  "The cache keyword will change the local directory.\n", PrgName);
  2257.         exit (0);
  2258.     }
  2259.  
  2260.     while (argc)
  2261.     {
  2262.     if (strcasecmp (*argv, "proxy") == 0)
  2263.     {
  2264.         if (argc < 3)
  2265.         {
  2266.         fprintf (stderr, "%s: need two arguments for 'proxy'\n", PrgName);
  2267.         exit (1);
  2268.         }
  2269.         argv++;
  2270.         argc -= 3;
  2271.         ProxyHost = *argv++;
  2272.         if ( (ProxyPort = atoi (*argv++)) <= 0)
  2273.         {
  2274.         fprintf (stderr, "%s: Wrong second argument for 'proxy' (need the port number).\n", PrgName);
  2275.         exit (1);
  2276.         }
  2277.     }
  2278.     else if (strcasecmp (*argv, "port") == 0)
  2279.     {
  2280.         if (argc < 2)
  2281.         {
  2282.         fprintf (stderr, "%s: need a argument for 'port'\n", PrgName);
  2283.         exit (1);
  2284.         }
  2285.         argv++;
  2286.         argc -= 2;
  2287.         ServerPort = atoi (*argv++);
  2288.     }
  2289.     else if (strcasecmp (*argv, "cache") == 0)
  2290.     {
  2291.         if (argc < 2)
  2292.         {
  2293.         fprintf (stderr, "%s: need a argument for 'cache'\n", PrgName);
  2294.         exit (1);
  2295.         }
  2296.         argv++;
  2297.         argc -= 2;
  2298.         if (chdir (*argv++))
  2299.         {
  2300.         fprintf (stderr, "%s: no directory '%s': %s\n", PrgName, argv[-1], strerror (errno));
  2301.         exit (1);
  2302.         }
  2303.     }
  2304.     else if (strcasecmp (*argv, "del") == 0)
  2305.     {
  2306.         if (argc < 2)
  2307.         {
  2308.         fprintf (stderr, "%s: need a argument for 'del'\n", PrgName);
  2309.         exit (1);
  2310.         }
  2311.         argv++;
  2312.         argc -= 2;
  2313.         DelCacheTime = atoi (*argv++);
  2314.     }
  2315.     else if (strcasecmp (*argv, "expire") == 0)
  2316.     {
  2317.         if (argc < 2)
  2318.         {
  2319.         fprintf (stderr, "%s: need a argument for 'expire'\n", PrgName);
  2320.         exit (1);
  2321.         }
  2322.         argv++;
  2323.         argc -= 2;
  2324.         ExpireCacheTime = atoi (*argv++);
  2325.     }
  2326.     else if (strcasecmp (*argv, "reload") == 0)
  2327.     {
  2328.         if (argc < 2)
  2329.         {
  2330.         fprintf (stderr, "%s: need a argument for 'reload'\n", PrgName);
  2331.         exit (1);
  2332.         }
  2333.         argv++;
  2334.         argc -= 2;
  2335.         ReloadCacheTime = atoi (*argv++);
  2336.     }
  2337.     else if (strcasecmp (*argv, "log") == 0)
  2338.     {
  2339.         if (argc < 2)
  2340.         {
  2341.         fprintf (stderr, "%s: need a argument for 'log'\n", PrgName);
  2342.         exit (1);
  2343.         }
  2344.         argv++;
  2345.         argc -= 2;
  2346.         LogFile = *argv++;
  2347.     }
  2348.     else if (strcasecmp (*argv, "numreq") == 0)
  2349.     {
  2350.         if (argc < 2)
  2351.         {
  2352.         fprintf (stderr, "%s: need a argument for 'numreq'\n", PrgName);
  2353.         exit (1);
  2354.         }
  2355.         argv++;
  2356.         argc -= 2;
  2357.         if ( (MaxRequests = atoi (*argv++)) <= MIN_REQUESTS)
  2358.         {
  2359.         fprintf (stderr, "%s: argument 'numreq' needs nummeric value > %d\n", PrgName, MIN_REQUESTS);
  2360.         exit (1);
  2361.         }
  2362.         if (MaxRequests * 3 >= FD_SETSIZE - 4)
  2363.         {
  2364.         fprintf (stderr, "%s: argument 'numreq' maximum value of %d exceeded\n", PrgName, (FD_SETSIZE - 5) / 3);
  2365.         exit (1);
  2366.         }
  2367.     }
  2368.     else if (strcasecmp (*argv, "unread") == 0)
  2369.     {
  2370.         argv++;
  2371.         argc--;
  2372.         CacheUnreadRequests = TRUE;
  2373.     }
  2374.     else if (strcasecmp (*argv, "offline") == 0)
  2375.     {
  2376.         argv++;
  2377.         argc--;
  2378.         if (GetQueued)
  2379.         {
  2380.         fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
  2381.         exit (1);
  2382.         }
  2383.         OffLine = 1;
  2384.     }
  2385.     else if (strcasecmp (*argv, "get") == 0)
  2386.     {
  2387.         argv++;
  2388.         argc--;
  2389.         if (OffLine)
  2390.         {
  2391.         fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
  2392.         exit (1);
  2393.         }
  2394.         GetQueued = 1;
  2395.     }
  2396.     else
  2397.     {
  2398.         fprintf (stderr, "%s: unknown option '%s'\n", PrgName, *argv);
  2399.         exit (1);
  2400.     }
  2401.     }
  2402.  
  2403.     assert (FD_SETSIZE > MaxRequests * 3);
  2404.  
  2405.     Init (ProxyHost, ProxyPort, LogFile);
  2406.  
  2407.     if (ReadCacheList ())               /* Read cache entries or rebuild them */
  2408.     BuildCache ();
  2409.  
  2410.     /* Never return */
  2411.     for (;;)
  2412.     {
  2413.     CheckGetQueued ();
  2414.     BuildFdSets (&ReadSet, &WriteSet);
  2415.     debug (("Select... RequestsFree %d, CachesFree %d\n", RequestsFree, CachesFree));
  2416.     if (select (FD_SETSIZE, &ReadSet, &WriteSet, NULL, NULL) <= 0)
  2417.     {
  2418.         if (errno == EINTR)
  2419.         {
  2420.         fprintf (stderr, "%s: terminating due to signal - saving cachetable\n", PrgName);
  2421.         fprintf (LogStream, "terminating due to signal - saving cachetable\n");
  2422.         DeleteInvalidCaches ();
  2423.         SaveCacheList ();
  2424.         fprintf (stderr, "done\n", PrgName);
  2425.         fprintf (LogStream, "done\n\n");
  2426.         fclose  (LogStream);
  2427.         exit (0);
  2428.         }
  2429.         syslog (LOG_ERR, "%s: select failed: %s", PrgName, strerror (errno));
  2430.     }
  2431.     else
  2432.     {
  2433.         ServRead (&ReadSet);
  2434.         ServWrite (&WriteSet);
  2435.     }
  2436.     }
  2437. }
  2438. /*)) */
  2439.  
  2440.